super-dsh 0.1.0 → 0.1.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 (178) hide show
  1. package/agent-agy/bridge/agy_bridge.py +250 -0
  2. package/agent-agy/bridge/gemini_openai_server.mjs +98 -0
  3. package/agent-agy/cordis.patch.yml +49 -0
  4. package/agent-agy/dist/adapter.d.ts +15 -0
  5. package/agent-agy/dist/adapter.js +42 -0
  6. package/agent-agy/dist/agent-preset-agy.d.ts +47 -0
  7. package/agent-agy/dist/agent-preset-agy.js +169 -0
  8. package/agent-agy/dist/agent-preset-projection.d.ts +31 -0
  9. package/agent-agy/dist/agent-preset-projection.js +33 -0
  10. package/agent-agy/dist/agent.d.ts +137 -0
  11. package/agent-agy/dist/agent.js +493 -0
  12. package/agent-agy/dist/agy-cli-client.d.ts +137 -0
  13. package/agent-agy/dist/agy-cli-client.js +301 -0
  14. package/agent-agy/dist/agy-client.d.ts +160 -0
  15. package/agent-agy/dist/agy-client.js +252 -0
  16. package/agent-agy/dist/agy-sessions.d.ts +24 -0
  17. package/agent-agy/dist/agy-sessions.js +116 -0
  18. package/agent-agy/dist/agy-store.d.ts +39 -0
  19. package/agent-agy/dist/agy-store.js +83 -0
  20. package/agent-agy/dist/inbox.d.ts +26 -0
  21. package/agent-agy/dist/inbox.js +53 -0
  22. package/agent-agy/dist/index.d.ts +80 -0
  23. package/agent-agy/dist/index.js +505 -0
  24. package/agent-agy/dist/models.d.ts +41 -0
  25. package/agent-agy/dist/models.js +74 -0
  26. package/agent-agy/dist/world-plugin.d.ts +9 -0
  27. package/agent-agy/dist/world-plugin.js +97 -0
  28. package/agent-agy/package.json +64 -0
  29. package/agent-claude/cordis.patch.yml +42 -0
  30. package/agent-claude/dist/adapter.js +62 -0
  31. package/agent-claude/dist/agent-preset-claude.js +169 -0
  32. package/agent-claude/dist/agent-preset-projection.js +30 -0
  33. package/agent-claude/dist/agent.js +1271 -0
  34. package/agent-claude/dist/claude-client.js +357 -0
  35. package/agent-claude/dist/claude-events.js +223 -0
  36. package/agent-claude/dist/claude-home.js +29 -0
  37. package/agent-claude/dist/commands.js +182 -0
  38. package/agent-claude/dist/content.js +134 -0
  39. package/agent-claude/dist/inbox.js +53 -0
  40. package/agent-claude/dist/index.js +555 -0
  41. package/agent-claude/dist/input-queue.js +64 -0
  42. package/agent-claude/dist/interaction.js +271 -0
  43. package/agent-claude/dist/models.js +158 -0
  44. package/agent-claude/dist/permission.js +120 -0
  45. package/agent-claude/dist/session-id.js +20 -0
  46. package/agent-claude/dist/session-map.js +165 -0
  47. package/agent-claude/dist/world-plugin.js +14 -0
  48. package/agent-claude/package.json +74 -0
  49. package/agent-codex/README.md +74 -0
  50. package/agent-codex/cordis.patch.yml +77 -0
  51. package/agent-codex/dist/adapter.js +55 -0
  52. package/agent-codex/dist/agent-preset-codex.js +171 -0
  53. package/agent-codex/dist/agent-preset-projection.js +37 -0
  54. package/agent-codex/dist/agent.js +1056 -0
  55. package/agent-codex/dist/app-home.js +65 -0
  56. package/agent-codex/dist/codex-client.js +469 -0
  57. package/agent-codex/dist/codex-events.js +297 -0
  58. package/agent-codex/dist/codex-store.js +190 -0
  59. package/agent-codex/dist/inbox.js +53 -0
  60. package/agent-codex/dist/index.js +605 -0
  61. package/agent-codex/dist/models.js +152 -0
  62. package/agent-codex/dist/permission.js +71 -0
  63. package/agent-codex/dist/session-map.js +170 -0
  64. package/agent-codex/dist/spike.js +46 -0
  65. package/agent-codex/dist/world-plugin.js +96 -0
  66. package/agent-codex/package.json +78 -0
  67. package/agent-hermes/README.md +144 -0
  68. package/agent-hermes/cordis.patch.yml +83 -0
  69. package/agent-hermes/dist/adapter.js +83 -0
  70. package/agent-hermes/dist/agent-preset-hermes.js +171 -0
  71. package/agent-hermes/dist/agent-preset-projection.js +34 -0
  72. package/agent-hermes/dist/agent.js +1068 -0
  73. package/agent-hermes/dist/hermes-client.js +953 -0
  74. package/agent-hermes/dist/hermes-events.js +289 -0
  75. package/agent-hermes/dist/hermes-store.js +158 -0
  76. package/agent-hermes/dist/inbox.js +53 -0
  77. package/agent-hermes/dist/index.js +541 -0
  78. package/agent-hermes/dist/models.js +324 -0
  79. package/agent-hermes/dist/permission.js +60 -0
  80. package/agent-hermes/dist/world-plugin.js +101 -0
  81. package/agent-hermes/package.json +76 -0
  82. package/agent-hub/cordis.patch.yml +12 -0
  83. package/agent-hub/dist/agent-roster.d.ts +34 -0
  84. package/agent-hub/dist/agent-roster.js +27 -0
  85. package/agent-hub/dist/carrier.d.ts +43 -0
  86. package/agent-hub/dist/carrier.js +241 -0
  87. package/agent-hub/dist/client-shim.d.ts +28 -0
  88. package/agent-hub/dist/client-shim.js +251 -0
  89. package/agent-hub/dist/envelope.d.ts +54 -0
  90. package/agent-hub/dist/envelope.js +92 -0
  91. package/agent-hub/dist/gateway.d.ts +28 -0
  92. package/agent-hub/dist/gateway.js +18 -0
  93. package/agent-hub/dist/index-pass.d.ts +22 -0
  94. package/agent-hub/dist/index-pass.js +47 -0
  95. package/agent-hub/dist/index.d.ts +29 -0
  96. package/agent-hub/dist/index.js +29 -0
  97. package/agent-hub/dist/labels.d.ts +45 -0
  98. package/agent-hub/dist/labels.js +83 -0
  99. package/agent-hub/dist/ownership.d.ts +37 -0
  100. package/agent-hub/dist/ownership.js +54 -0
  101. package/agent-hub/dist/roster.d.ts +28 -0
  102. package/agent-hub/dist/roster.js +38 -0
  103. package/agent-hub/dist/rpc.d.ts +38 -0
  104. package/agent-hub/dist/rpc.js +52 -0
  105. package/agent-hub/dist/spawn-world.d.ts +46 -0
  106. package/agent-hub/dist/spawn-world.js +72 -0
  107. package/agent-hub/dist/targets.d.ts +33 -0
  108. package/agent-hub/dist/targets.js +30 -0
  109. package/agent-hub/dist/world-entry.d.ts +31 -0
  110. package/agent-hub/dist/world-entry.js +51 -0
  111. package/agent-hub/dist/world-host.d.ts +69 -0
  112. package/agent-hub/dist/world-host.js +44 -0
  113. package/agent-hub/dist/world-join.d.ts +16 -0
  114. package/agent-hub/dist/world-join.js +122 -0
  115. package/agent-hub/dist/world-mount.d.ts +52 -0
  116. package/agent-hub/dist/world-mount.js +52 -0
  117. package/agent-hub/dist/world-mux.d.ts +72 -0
  118. package/agent-hub/dist/world-mux.js +224 -0
  119. package/agent-hub/dist/world-provision.d.ts +20 -0
  120. package/agent-hub/dist/world-provision.js +122 -0
  121. package/agent-hub/dist/world-web-server.d.ts +162 -0
  122. package/agent-hub/dist/world-web-server.js +248 -0
  123. package/agent-hub/lib/client/index.js +242 -0
  124. package/agent-hub/package.json +65 -0
  125. package/agent-omp/README.md +77 -0
  126. package/agent-omp/cordis.patch.yml +92 -0
  127. package/agent-omp/dist/adapter.js +36 -0
  128. package/agent-omp/dist/agent-preset-omp.js +171 -0
  129. package/agent-omp/dist/agent-preset-projection.js +36 -0
  130. package/agent-omp/dist/agent.js +1145 -0
  131. package/agent-omp/dist/app-home.js +86 -0
  132. package/agent-omp/dist/discovery.js +116 -0
  133. package/agent-omp/dist/inbox.js +53 -0
  134. package/agent-omp/dist/index.js +574 -0
  135. package/agent-omp/dist/knobs.js +50 -0
  136. package/agent-omp/dist/lazy-rpc.js +161 -0
  137. package/agent-omp/dist/mobile/gesture.js +148 -0
  138. package/agent-omp/dist/mobile/zoom-guard.js +294 -0
  139. package/agent-omp/dist/mobile-boot.js +38 -0
  140. package/agent-omp/dist/models.js +299 -0
  141. package/agent-omp/dist/omp-cli.js +147 -0
  142. package/agent-omp/dist/omp-disk-discovery.js +178 -0
  143. package/agent-omp/dist/omp-store.js +518 -0
  144. package/agent-omp/dist/pairing.js +45 -0
  145. package/agent-omp/dist/permission.js +96 -0
  146. package/agent-omp/dist/protocol.js +13 -0
  147. package/agent-omp/dist/rpc-types.js +1 -0
  148. package/agent-omp/dist/sdk-client.js +378 -0
  149. package/agent-omp/dist/sidecar-client.js +195 -0
  150. package/agent-omp/dist/store/adapter.js +47 -0
  151. package/agent-omp/dist/store/db.js +210 -0
  152. package/agent-omp/dist/store/index.js +59 -0
  153. package/agent-omp/dist/store/reconcile.js +51 -0
  154. package/agent-omp/dist/store/schema.js +40 -0
  155. package/agent-omp/dist/world-plugin.js +96 -0
  156. package/agent-omp/package.json +97 -0
  157. package/agent-omp/sidecar/main.ts +481 -0
  158. package/agent-pi/README.md +67 -0
  159. package/agent-pi/cordis.patch.yml +80 -0
  160. package/agent-pi/dist/adapter.js +53 -0
  161. package/agent-pi/dist/agent-preset-pi.js +169 -0
  162. package/agent-pi/dist/agent-preset-projection.js +29 -0
  163. package/agent-pi/dist/agent.js +949 -0
  164. package/agent-pi/dist/inbox.js +53 -0
  165. package/agent-pi/dist/index.js +595 -0
  166. package/agent-pi/dist/knobs.js +24 -0
  167. package/agent-pi/dist/models.js +200 -0
  168. package/agent-pi/dist/permission.js +66 -0
  169. package/agent-pi/dist/pi-client.js +224 -0
  170. package/agent-pi/dist/pi-events.js +155 -0
  171. package/agent-pi/dist/pi-home.js +55 -0
  172. package/agent-pi/dist/safe-json.js +25 -0
  173. package/agent-pi/dist/session-map.js +119 -0
  174. package/agent-pi/dist/world-plugin.js +97 -0
  175. package/agent-pi/package.json +77 -0
  176. package/cordis.patch.yml +22 -19
  177. package/package.json +88 -25
  178. package/client.js +0 -4
@@ -0,0 +1,250 @@
1
+ #!/usr/bin/env python3
2
+ # agy_bridge.py — JSONL-over-stdio bridge hosting the google-antigravity SDK.
3
+ #
4
+ # Contract (one JSON object per line; requests on stdin, events on stdout):
5
+ # -> {"op":"start", "model":str, "thinking_level"?:"minimal|low|medium|high|extra_high",
6
+ # "api_key"?:str, "project"?:str, "location"?:str,
7
+ # "approval_mode"?:"allow"|"ask", # default "allow"
8
+ # "workspaces":[str], "save_dir"?:str}
9
+ # (localharness is NOT spawned here — materializes lazily on first prompt)
10
+ # -> {"op":"prompt", "text":str, "conversation_id"?:str}
11
+ # streams back:
12
+ # <- {"event":"thinking","text":str} # Thought delta (reasoning)
13
+ # <- {"event":"chunk","text":str} # Text delta
14
+ # <- {"event":"tool","name":str} # tool dispatched
15
+ # <- {"event":"approval_request","id":str,"tool":str,"args":str} # ask mode
16
+ # <- {"event":"usage","input_tokens":int,"output_tokens":int,
17
+ # "thinking_tokens":int,"total_tokens":int} # best effort
18
+ # <- {"event":"done","conversation_id":str,"turn_ms":int}
19
+ # <- {"event":"error","error":str,"recoverable":bool}
20
+ # -> {"op":"approval","id":str,"allow":bool} # host reply to approval_request
21
+ # -> {"op":"ping"} -> {"event":"pong"}
22
+ # -> {"op":"close"} -> exits 0
23
+ #
24
+ # Auth is transparent: pass api_key (Gemini API key) or project (+ADC) for the
25
+ # Vertex endpoint. CLI (agy) is never invoked.
26
+
27
+ import asyncio
28
+ import json
29
+ import os
30
+ import sys
31
+ import time
32
+ import uuid
33
+
34
+
35
+ def emit(obj):
36
+ sys.stdout.write(json.dumps(obj, ensure_ascii=False) + "\n")
37
+ sys.stdout.flush()
38
+
39
+
40
+ class Bridge:
41
+ def __init__(self):
42
+ self.config = None
43
+ self.started = False
44
+ self.pending_approvals = {} # id -> asyncio.Event
45
+ self.approval_results = {} # id -> bool
46
+ self.approval_mode = "allow"
47
+
48
+ async def handle_start(self, req):
49
+ import google.antigravity as ga
50
+
51
+ kwargs = {
52
+ "model": req.get("model") or "gemini-3.5-flash",
53
+ "workspaces": req.get("workspaces") or [os.getcwd()],
54
+ }
55
+ if req.get("save_dir"):
56
+ kwargs["save_dir"] = req["save_dir"]
57
+ if req.get("api_key"):
58
+ kwargs["api_key"] = req["api_key"]
59
+ if req.get("project"):
60
+ kwargs["vertex"] = True
61
+ kwargs["project"] = req["project"]
62
+ if req.get("location"):
63
+ kwargs["location"] = req["location"]
64
+ self.approval_mode = req.get("approval_mode") or "allow"
65
+ # ask mode requires the SDK to keep hook objects in-process; the current
66
+ # localharness pickles parts of the config, which fails on runtime
67
+ # references (SimpleQueue). Opt-in via AGY_BRIDGE_ASK=1 until the SDK
68
+ # supports in-process hooks; otherwise degrade to allow with a trace.
69
+ if self.approval_mode == "ask":
70
+ if os.environ.get("AGY_BRIDGE_ASK") == "1":
71
+ kwargs["hooks"] = list(kwargs.get("hooks") or []) + [PreToolApprovalBridge(self)]
72
+ else:
73
+ emit({"event": "error", "error": "approval_mode=ask unavailable (SDK pickles config; set AGY_BRIDGE_ASK=1 to force); degrading to allow", "recoverable": True})
74
+ self.approval_mode = "allow"
75
+ level = req.get("thinking_level")
76
+ if level:
77
+ from google.antigravity.models import (
78
+ GeminiAPIEndpoint, GeminiModelOptions, ModelTarget, VertexEndpoint,
79
+ )
80
+ from google.antigravity.types import ThinkingLevel
81
+ if kwargs.get("vertex"):
82
+ endpoint = VertexEndpoint(project=kwargs.get("project"),
83
+ location=kwargs.get("location"))
84
+ else:
85
+ endpoint = GeminiAPIEndpoint(api_key=kwargs.get("api_key"))
86
+ kwargs["model"] = None
87
+ kwargs["models"] = [ModelTarget(
88
+ name=req["model"],
89
+ endpoint=endpoint,
90
+ options=GeminiModelOptions(thinking_level=ThinkingLevel(level)),
91
+ )]
92
+ self.config = ga.LocalAgentConfig(**kwargs)
93
+ self.started = True
94
+ emit({"event": "started"})
95
+
96
+ async def handle_prompt(self, req):
97
+ if not self.started:
98
+ emit({"event": "error", "error": "not started", "recoverable": False})
99
+ return
100
+ import google.antigravity as ga
101
+
102
+ text = req.get("text") or ""
103
+ cid = req.get("conversation_id")
104
+ t0 = time.time()
105
+ agent = ga.Agent(self.config)
106
+ try:
107
+ await agent.__aenter__()
108
+ resp = await agent.chat(text)
109
+ usage = None
110
+ # resp.__aiter__ yields only Text strings; .chunks carries the full
111
+ # typed stream (Thought / ToolCall / ToolResult).
112
+ async for ch in resp.chunks:
113
+ kind = type(ch).__name__
114
+ if isinstance(ch, str):
115
+ emit({"event": "chunk", "text": ch})
116
+ elif kind == "Thought":
117
+ emit({"event": "thinking", "text": getattr(ch, "text", "")})
118
+ elif kind == "Text":
119
+ emit({"event": "chunk", "text": getattr(ch, "text", "")})
120
+ elif kind == "ToolCall":
121
+ emit({"event": "tool_call", "id": str(getattr(ch, "id", "") or ""),
122
+ "name": str(getattr(ch, "name", "") or "unknown"),
123
+ "args": json.dumps(getattr(ch, "args", ""), ensure_ascii=False, default=str)[:600]})
124
+ elif kind == "ToolResult":
125
+ err = getattr(ch, "error", None)
126
+ emit({"event": "tool_result", "id": str(getattr(ch, "id", "") or ""),
127
+ "name": str(getattr(ch, "name", "") or ""),
128
+ "is_error": err is not None,
129
+ "result": str(getattr(ch, "result", ""))[:600]})
130
+ conv = agent.conversation
131
+ new_cid = (conv.conversation_id if conv else None) or cid or ""
132
+ if conv is not None:
133
+ u = getattr(conv, "last_turn_usage", None)
134
+ if callable(u):
135
+ u = u()
136
+ if u is not None:
137
+ d = u.model_dump() if hasattr(u, "model_dump") else {}
138
+ emit({"event": "usage",
139
+ "input_tokens": int(d.get("prompt_token_count") or 0),
140
+ "output_tokens": int(d.get("candidates_token_count") or 0),
141
+ "thinking_tokens": int(d.get("thoughts_token_count") or 0),
142
+ "total_tokens": int(d.get("total_token_count") or 0)})
143
+ emit({"event": "done", "conversation_id": new_cid,
144
+ "turn_ms": int((time.time() - t0) * 1000)})
145
+ except Exception as e: # noqa: BLE001 — surface everything to the host
146
+ emit({"event": "error", "error": f"{type(e).__name__}: {e}",
147
+ "recoverable": True})
148
+ finally:
149
+ try:
150
+ await agent.__aexit__(None, None, None)
151
+ except Exception:
152
+ pass
153
+
154
+ async def handle_approval(self, req):
155
+ aid = req.get("id")
156
+ if aid in self.pending_approvals:
157
+ self.approval_results[aid] = bool(req.get("allow"))
158
+ self.pending_approvals.pop(aid).set()
159
+
160
+ async def wait_approval(self, aid, timeout=120.0):
161
+ ev = asyncio.Event()
162
+ self.pending_approvals[aid] = ev
163
+ try:
164
+ await asyncio.wait_for(ev.wait(), timeout)
165
+ return self.approval_results.get(aid, False)
166
+ except asyncio.TimeoutError:
167
+ self.pending_approvals.pop(aid, None)
168
+ return False
169
+
170
+ async def main_loop(self):
171
+ loop = asyncio.get_event_loop()
172
+
173
+ async def read_stdin():
174
+ # Dedicated reader: approval ops must arrive WHILE a prompt is
175
+ # in flight (the prompt handler blocks its own coroutine).
176
+ while True:
177
+ line = await loop.run_in_executor(None, sys.stdin.readline)
178
+ if not line:
179
+ self.host_gone = True
180
+ return
181
+ line = line.strip()
182
+ if not line:
183
+ continue
184
+ try:
185
+ req = json.loads(line)
186
+ except json.JSONDecodeError as e:
187
+ emit({"event": "error", "error": f"bad json: {e}", "recoverable": False})
188
+ continue
189
+ op = req.get("op")
190
+ if op == "approval":
191
+ await self.handle_approval(req)
192
+ elif op == "ping":
193
+ emit({"event": "pong"})
194
+ else:
195
+ await self.ops.put(req) # close included: processed in order
196
+
197
+ self.host_gone = False
198
+ self.ops = asyncio.Queue()
199
+ reader = asyncio.ensure_future(read_stdin())
200
+ try:
201
+ while not self.host_gone:
202
+ req = await self.ops.get()
203
+ op = req.get("op")
204
+ if op == "start":
205
+ await self.handle_start(req)
206
+ elif op == "prompt":
207
+ await self.handle_prompt(req)
208
+ elif op == "close":
209
+ emit({"event": "closed"})
210
+ return
211
+ else:
212
+ emit({"event": "error", "error": f"unknown op {op!r}", "recoverable": False})
213
+ finally:
214
+ reader.cancel()
215
+
216
+
217
+ def _sdk_hooks():
218
+ from google.antigravity import hooks as hooks_mod
219
+ return hooks_mod
220
+
221
+
222
+ try:
223
+ _HOOKS = _sdk_hooks()
224
+ _DECIDE_BASE = _HOOKS.PreToolCallDecideHook
225
+ except Exception: # pragma: no cover — SDK absent in pure-syntax checks
226
+ _DECIDE_BASE = object
227
+
228
+
229
+ class PreToolApprovalBridge(_DECIDE_BASE):
230
+ """Ask the host over the bridge protocol before every tool call (ask mode)."""
231
+
232
+ def __init__(self, bridge: Bridge):
233
+ self.bridge = bridge
234
+
235
+ async def run(self, context, data): # SDK DecideHook protocol
236
+ aid = uuid.uuid4().hex
237
+ emit({"event": "approval_request", "id": aid,
238
+ "tool": str(getattr(data, "name", "unknown")),
239
+ "args": str(getattr(data, "args", "") or "")[:400]})
240
+ allow = await self.bridge.wait_approval(aid)
241
+ return _HOOKS.HookResult(allow=allow, message=None if allow else "denied by user")
242
+
243
+
244
+ async def main():
245
+ bridge = Bridge()
246
+ await bridge.main_loop()
247
+
248
+
249
+ if __name__ == "__main__":
250
+ asyncio.run(main())
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * gemini_openai_server.mjs — SPIKE: minimal Gemini-protocol front → OpenAI
4
+ * chat/completions back. Enough surface for the google-antigravity SDK's
5
+ * generateContent path: text messages, system instruction, SSE streaming,
6
+ * usage passthrough. Tools / multimodal are NOT translated (spike scope).
7
+ *
8
+ * Usage: node gemini_openai_server.mjs <listenPort> <openaiBaseUrl> <openaiKey> <openaiModel>
9
+ */
10
+ import http from "node:http";
11
+
12
+ const [port, oaBase, oaKey, oaModel] = process.argv.slice(2);
13
+ const UP = `${oaBase.replace(/\/$/, "")}/chat/completions`;
14
+
15
+ function g2o(req) {
16
+ const messages = [];
17
+ const sys = req.systemInstruction ?? req.system_instruction;
18
+ const sysText = sys ? partsText(sys.parts ?? sys.parts === undefined ? sys.parts ?? [] : []) : "";
19
+ if (sysText) messages.push({ role: "system", content: sysText });
20
+ for (const c of req.contents ?? []) {
21
+ const t = partsText(c.parts ?? []);
22
+ if (t) messages.push({ role: c.role === "model" ? "assistant" : "user", content: t });
23
+ }
24
+ return { model: oaModel, messages, stream: req.__stream === true };
25
+ }
26
+ function partsText(parts) {
27
+ return (parts ?? []).map((p) => (typeof p === "string" ? p : p.text ?? "")).join("");
28
+ }
29
+ function o2g(oa, model) {
30
+ return {
31
+ candidates: [{
32
+ content: { role: "model", parts: [{ text: oa.choices?.[0]?.message?.content ?? "" }] },
33
+ finishReason: oa.choices?.[0]?.finish_reason === "length" ? "MAX_TOKENS" : "STOP",
34
+ }],
35
+ usageMetadata: oa.usage
36
+ ? { promptTokenCount: oa.usage.prompt_tokens, candidatesTokenCount: oa.usage.completion_tokens, totalTokenCount: oa.usage.total_tokens }
37
+ : undefined,
38
+ modelVersion: model,
39
+ };
40
+ }
41
+ async function callUpstream(body) {
42
+ const r = await fetch(UP, {
43
+ method: "POST",
44
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${oaKey}` },
45
+ body: JSON.stringify(body),
46
+ });
47
+ if (!r.ok) throw new Error(`upstream ${r.status}: ${(await r.text()).slice(0, 200)}`);
48
+ return r;
49
+ }
50
+
51
+ const server = http.createServer(async (req, res) => {
52
+ const url = new URL(req.url, "http://x");
53
+ if (req.method === "GET" && url.pathname.endsWith("/models")) {
54
+ res.setHeader("Content-Type", "application/json");
55
+ res.end(JSON.stringify({ models: [{ name: `models/${oaModel}`, supportedGenerationMethods: ["generateContent", "streamGenerateContent"] }] }));
56
+ return;
57
+ }
58
+ if (req.method !== "POST") { res.writeHead(404).end(); return; }
59
+ let raw = "";
60
+ for await (const c of req) raw += c;
61
+ let g; try { g = JSON.parse(raw); } catch { res.writeHead(400).end(); return; }
62
+ const streaming = url.pathname.includes("streamGenerateContent");
63
+ const body = g2o({ ...g, __stream: streaming });
64
+ try {
65
+ if (!streaming) {
66
+ const oa = await (await callUpstream(body)).json();
67
+ res.setHeader("Content-Type", "application/json");
68
+ res.end(JSON.stringify(o2g(oa, oa.model)));
69
+ } else {
70
+ res.writeHead(200, { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" });
71
+ const up = await callUpstream(body);
72
+ const dec = new TextDecoder();
73
+ let buf = "";
74
+ for await (const chunk of up.body) {
75
+ buf += dec.decode(chunk, { stream: true });
76
+ let i;
77
+ while ((i = buf.indexOf("\n")) >= 0) {
78
+ const line = buf.slice(0, i).trim(); buf = buf.slice(i + 1);
79
+ if (!line.startsWith("data:")) continue;
80
+ const payload = line.slice(5).trim();
81
+ if (payload === "[DONE]") continue;
82
+ const oa = JSON.parse(payload);
83
+ const delta = oa.choices?.[0]?.delta?.content ?? "";
84
+ const ev = {
85
+ candidates: [{ content: { role: "model", parts: [{ text: delta }] } }],
86
+ };
87
+ if (oa.usage) ev.usageMetadata = { promptTokenCount: oa.usage.prompt_tokens, candidatesTokenCount: oa.usage.completion_tokens, totalTokenCount: oa.usage.total_tokens };
88
+ res.write(`data: ${JSON.stringify(ev)}\n\n`);
89
+ }
90
+ }
91
+ res.end();
92
+ }
93
+ } catch (e) {
94
+ res.writeHead(502, { "Content-Type": "application/json" });
95
+ res.end(JSON.stringify({ error: { code: 502, message: String(e.message ?? e) } }));
96
+ }
97
+ });
98
+ server.listen(Number(port), "127.0.0.1", () => console.log(`gemini-openai spike on ${port} -> ${oaModel}`));
@@ -0,0 +1,49 @@
1
+ # @pgmi-builds/agent-adapter-agy bundle patch (AW-G SDK line, pi/hermes form).
2
+ # The `dsh.bundle.patch` layer of the adapter package: mounts the agy provider
3
+ # and disables the native components it replaces. Port/webserver posture is
4
+ # NOT here (worldMountPatches owns it); the world profile patch pins the home.
5
+ #
6
+ # Auth (2026-09-18 consumer-first ruling, dev-rules §16a): Gemini API key in
7
+ # adapter world-home state (or GEMINI_API_KEY env), else ADC — the SDK consumes
8
+ # either transparently. The agy CLI is never invoked.
9
+
10
+ - insert:
11
+ - id: agy-provider
12
+ name: '@pgmi-builds/agent-adapter-agy'
13
+
14
+ # The SDK bridge owns the live transcript; disable the built-in loop so it
15
+ # does not double-run alongside the bridge session.
16
+ - id: agent-loop
17
+ disabled: true
18
+
19
+ # The agy provider registers the single `agy` route on ctx.llm; leaving the
20
+ # native deepseek row mounted would advertise foreign models in this world.
21
+ - id: llm-deepseek
22
+ disabled: true
23
+
24
+ # Single-preset roster like pi: launch-only permission mapping (headless bridge
25
+ # has no per-action approval channel). Full 3-preset restatement per §4.1.
26
+ - id: agent-presets
27
+ disabled: true
28
+ - id: permission
29
+ config:
30
+ presets:
31
+ read-only:
32
+ sandbox: read-only
33
+ approval: ask
34
+ workspace-write:
35
+ sandbox: workspace-write
36
+ approval: ask
37
+ danger-full-access:
38
+ sandbox: danger-full-access
39
+ approval: never
40
+ defaultPreset: danger-full-access
41
+
42
+ # Directory picker: pin both faces of `browse`, unmount the auto row (§4.1).
43
+ - insert:
44
+ - id: directory-picker-browse
45
+ name: '@deepseek-ai/dsh-host-directory-picker-browse'
46
+ - id: directory-picker-browse-surface
47
+ name: '@deepseek-ai/dsh-client-ui-directory-picker-browse'
48
+ - id: directory-picker
49
+ disabled: true
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Antigravity-backed `LlmAdapter` registered on `ctx.llm` by the Agy provider.
3
+ * Catalog is STATIC (models.ts): the SDK has no model-list API and the CLI
4
+ * catalog path is auth-gated (dev-rules §16a), so every surface is synchronous.
5
+ * `stream` is never dispatched — the Antigravity agent owns generation through
6
+ * its bridge child (`AgyAgent`); it throws rather than fabricating a wire route.
7
+ */
8
+ import { LlmAdapter, type GenerateOptions, type LlmModelInfo, type LlmProviderInfo, type LlmResolvedModelInfo, type StreamChunk } from "@deepseek-ai/dsh-llm";
9
+ export declare const AGY_PROVIDER_ID = "agy";
10
+ export declare class AgyLlmAdapter extends LlmAdapter {
11
+ providerInfo(provider: string): LlmProviderInfo;
12
+ listModels(provider: string): Promise<readonly LlmModelInfo[]>;
13
+ resolveModel(provider: string, model: string): Promise<LlmResolvedModelInfo>;
14
+ stream(_options: GenerateOptions): AsyncIterable<StreamChunk>;
15
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Antigravity-backed `LlmAdapter` registered on `ctx.llm` by the Agy provider.
3
+ * Catalog is STATIC (models.ts): the SDK has no model-list API and the CLI
4
+ * catalog path is auth-gated (dev-rules §16a), so every surface is synchronous.
5
+ * `stream` is never dispatched — the Antigravity agent owns generation through
6
+ * its bridge child (`AgyAgent`); it throws rather than fabricating a wire route.
7
+ */
8
+ import { LlmAdapter, LlmError, ReasoningEffortId, } from "@deepseek-ai/dsh-llm";
9
+ import { AGY_MODEL_CATALOG } from "./models.js";
10
+ export const AGY_PROVIDER_ID = "agy";
11
+ export class AgyLlmAdapter extends LlmAdapter {
12
+ providerInfo(provider) {
13
+ return { id: provider, name: provider === AGY_PROVIDER_ID ? "Google" : provider };
14
+ }
15
+ async listModels(provider) {
16
+ return AGY_MODEL_CATALOG.map((model) => ({
17
+ provider,
18
+ id: model.slug,
19
+ name: model.label,
20
+ }));
21
+ }
22
+ async resolveModel(provider, model) {
23
+ const found = AGY_MODEL_CATALOG.find((candidate) => candidate.slug === model);
24
+ if (found === undefined) {
25
+ throw new LlmError(`Agy provider "${provider}" does not serve model "${model}"`, "MODEL_NOT_FOUND");
26
+ }
27
+ return {
28
+ provider,
29
+ id: found.slug,
30
+ name: found.label,
31
+ ...(found.efforts === undefined ? {} : {
32
+ reasoning: {
33
+ efforts: found.efforts.map((level) => ({ id: ReasoningEffortId(level), name: level })),
34
+ ...(found.defaultEffort === undefined ? {} : { defaultEffort: ReasoningEffortId(found.defaultEffort) }),
35
+ },
36
+ }),
37
+ };
38
+ }
39
+ stream(_options) {
40
+ throw new LlmError("the Agy adapter does not stream: Antigravity owns generation through its bridge agent", "UNSUPPORTED_STREAM");
41
+ }
42
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * SingleAgyPresetRoster — the profile's `agentPresets` service.
3
+ *
4
+ * Antigravity is single-mode: the agy CLI/SDK bridge drives its own tools, so
5
+ * no Dash-side preset composition exists to choose from. But the web surfaces
6
+ * (new-session mode dropdown, Settings → Agent Preset) render from the roster
7
+ * RPC, and an absent roster renders "empty" rather than "one mode". This
8
+ * service publishes exactly one non-authorable preset, `Antigravity`, whose
9
+ * composition is deliberately empty — mounting it composes nothing, exactly
10
+ * the rosterless behavior the provider runs on.
11
+ *
12
+ * REGISTRATION LESSON (ported verbatim from the omp bridge via hermes): this
13
+ * service must be constructed on the PROVIDER's own top-level fiber, never
14
+ * under `ctx.plugin(...)` — a child fiber hides its @Remote routes from the
15
+ * API gateway's root-level remote enumeration and every agentPresets/* call
16
+ * 404s.
17
+ */
18
+ import { type Context } from "@deepseek-ai/cordis";
19
+ import { TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
20
+ import { type Agent } from "@deepseek-ai/dsh-agent";
21
+ import { type AgentPreset, type AgentPresetDocument, type AgentPresetRoster } from "@deepseek-ai/dsh-agent-presets";
22
+ import type { ScopeKey } from "@deepseek-ai/dsh-scope";
23
+ export declare class SingleAgyPresetRoster extends TypertRemoteService {
24
+ constructor(ctx: Context);
25
+ get defaultId(): string;
26
+ get roots(): readonly never[];
27
+ get authorable(): boolean;
28
+ list(): Promise<AgentPreset[]>;
29
+ resolve(id?: string): Promise<AgentPreset>;
30
+ mount(_agentCtx: Context, id?: string): Promise<AgentPreset>;
31
+ recompose(_agentCtx: Context, id: string): Promise<AgentPreset>;
32
+ read(id: string): Promise<string>;
33
+ remoteList(): Promise<AgentPresetRoster>;
34
+ remoteRead(agentPreset: string): Promise<AgentPresetDocument>;
35
+ remoteCopy(from: string, id: string, name?: string): Promise<void>;
36
+ remoteDelete(id: string): Promise<void>;
37
+ /** Fixed roster: one preset, so selecting it is a no-op ack; anything else is unknown. */
38
+ remoteSelect(agent: Agent, agentPreset: string): Promise<string>;
39
+ copy(_from: string, id: string, _name?: string): Promise<void>;
40
+ remove(id: string): Promise<void>;
41
+ serviceFor(_agent: {
42
+ ctx: Context;
43
+ }, _name: string): undefined;
44
+ composeFrom(_agentCtx: Context, _parentCtx: Context): undefined;
45
+ composedPreset(_agentCtx: Context): string | undefined;
46
+ standingKeyFor(id?: string): Promise<ScopeKey>;
47
+ }
@@ -0,0 +1,169 @@
1
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
2
+ var useValue = arguments.length > 2;
3
+ for (var i = 0; i < initializers.length; i++) {
4
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
5
+ }
6
+ return useValue ? value : void 0;
7
+ };
8
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
9
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
10
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
11
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
12
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
13
+ var _, done = false;
14
+ for (var i = decorators.length - 1; i >= 0; i--) {
15
+ var context = {};
16
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
17
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
18
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
19
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
20
+ if (kind === "accessor") {
21
+ if (result === void 0) continue;
22
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
23
+ if (_ = accept(result.get)) descriptor.get = _;
24
+ if (_ = accept(result.set)) descriptor.set = _;
25
+ if (_ = accept(result.init)) initializers.unshift(_);
26
+ }
27
+ else if (_ = accept(result)) {
28
+ if (kind === "field") initializers.unshift(_);
29
+ else descriptor[key] = _;
30
+ }
31
+ }
32
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
33
+ done = true;
34
+ };
35
+ import { Remote, RemoteError, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
36
+ const AGY_PRESET = Object.freeze({
37
+ id: "agy",
38
+ trust: "system",
39
+ path: "",
40
+ name: "Antigravity",
41
+ description: "Google Antigravity (Gemini) via the agent-agy adapter",
42
+ });
43
+ const COMPOSITION_TEXT = [
44
+ "# Antigravity (fixed roster)",
45
+ "",
46
+ "This deployment runs exactly one agent mode: the Antigravity provider bridges Dash",
47
+ "sessions to the google-antigravity runtime (CLI stream-json primary, SDK bridge",
48
+ "fallback). There is no per-session plugin composition to configure, so this",
49
+ "preset mounts nothing.",
50
+ "",
51
+ "rows: []",
52
+ "",
53
+ ].join("\n");
54
+ /** The standing scope key every `agy` session reads presenters through. */
55
+ const STANDING_KEY = {};
56
+ let SingleAgyPresetRoster = (() => {
57
+ let _classSuper = TypertRemoteService;
58
+ let _instanceExtraInitializers = [];
59
+ let _remoteList_decorators;
60
+ let _remoteRead_decorators;
61
+ let _remoteCopy_decorators;
62
+ let _remoteDelete_decorators;
63
+ let _remoteSelect_decorators;
64
+ return class SingleAgyPresetRoster extends _classSuper {
65
+ static {
66
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
67
+ _remoteList_decorators = [Remote("list")];
68
+ _remoteRead_decorators = [Remote("read")];
69
+ _remoteCopy_decorators = [Remote("copy")];
70
+ _remoteDelete_decorators = [Remote("deletePreset")];
71
+ _remoteSelect_decorators = [Remote("select")];
72
+ __esDecorate(this, null, _remoteList_decorators, { kind: "method", name: "remoteList", static: false, private: false, access: { has: obj => "remoteList" in obj, get: obj => obj.remoteList }, metadata: _metadata }, null, _instanceExtraInitializers);
73
+ __esDecorate(this, null, _remoteRead_decorators, { kind: "method", name: "remoteRead", static: false, private: false, access: { has: obj => "remoteRead" in obj, get: obj => obj.remoteRead }, metadata: _metadata }, null, _instanceExtraInitializers);
74
+ __esDecorate(this, null, _remoteCopy_decorators, { kind: "method", name: "remoteCopy", static: false, private: false, access: { has: obj => "remoteCopy" in obj, get: obj => obj.remoteCopy }, metadata: _metadata }, null, _instanceExtraInitializers);
75
+ __esDecorate(this, null, _remoteDelete_decorators, { kind: "method", name: "remoteDelete", static: false, private: false, access: { has: obj => "remoteDelete" in obj, get: obj => obj.remoteDelete }, metadata: _metadata }, null, _instanceExtraInitializers);
76
+ __esDecorate(this, null, _remoteSelect_decorators, { kind: "method", name: "remoteSelect", static: false, private: false, access: { has: obj => "remoteSelect" in obj, get: obj => obj.remoteSelect }, metadata: _metadata }, null, _instanceExtraInitializers);
77
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
78
+ }
79
+ constructor(ctx) {
80
+ super(ctx, "agentPresets");
81
+ __runInitializers(this, _instanceExtraInitializers);
82
+ }
83
+ get defaultId() {
84
+ return AGY_PRESET.id;
85
+ }
86
+ get roots() {
87
+ return [];
88
+ }
89
+ get authorable() {
90
+ return false;
91
+ }
92
+ async list() {
93
+ return [AGY_PRESET];
94
+ }
95
+ async resolve(id) {
96
+ if (id === undefined || id === AGY_PRESET.id)
97
+ return AGY_PRESET;
98
+ throw new RemoteError("agent-preset/not-found", `agent-presets: preset "${id}" not found (available: agy)`, { agentPreset: id, available: [AGY_PRESET.id] });
99
+ }
100
+ async mount(_agentCtx, id) {
101
+ return this.resolve(id);
102
+ }
103
+ async recompose(_agentCtx, id) {
104
+ return this.resolve(id);
105
+ }
106
+ async read(id) {
107
+ await this.resolve(id);
108
+ return COMPOSITION_TEXT;
109
+ }
110
+ // ── Remote RPC surface (alpha: agentPresets routes come from @Remote) ────
111
+ async remoteList() {
112
+ if (process.env.AGY_TRACE === "1")
113
+ process.stderr.write("[agy-roster] remoteList INVOKED\n");
114
+ return {
115
+ presets: [
116
+ {
117
+ id: AGY_PRESET.id,
118
+ trust: AGY_PRESET.trust,
119
+ isDefault: true,
120
+ name: AGY_PRESET.name,
121
+ description: AGY_PRESET.description,
122
+ },
123
+ ],
124
+ authorable: this.authorable,
125
+ };
126
+ }
127
+ async remoteRead(agentPreset) {
128
+ const preset = await this.resolve(agentPreset);
129
+ return {
130
+ agentPreset: preset.id,
131
+ trust: preset.trust,
132
+ content: await this.read(preset.id),
133
+ name: preset.name,
134
+ description: preset.description,
135
+ };
136
+ }
137
+ async remoteCopy(from, id, name) {
138
+ await this.copy(from, id, name);
139
+ }
140
+ async remoteDelete(id) {
141
+ await this.remove(id);
142
+ }
143
+ /** Fixed roster: one preset, so selecting it is a no-op ack; anything else is unknown. */
144
+ async remoteSelect(agent, agentPreset) {
145
+ const preset = await this.resolve(agentPreset);
146
+ return preset.id;
147
+ }
148
+ async copy(_from, id, _name) {
149
+ throw new RemoteError("agent-preset/read-only", `agent-presets: preset "${id}" cannot be written: the Antigravity roster is fixed and ships exactly one preset`, { agentPreset: id, reason: "the Antigravity roster is fixed and ships exactly one preset" });
150
+ }
151
+ async remove(id) {
152
+ throw new RemoteError("agent-preset/read-only", `agent-presets: preset "${id}" cannot be written: the Antigravity roster is fixed and ships exactly one preset`, { agentPreset: id, reason: "the Antigravity roster is fixed and ships exactly one preset" });
153
+ }
154
+ serviceFor(_agent, _name) {
155
+ return undefined;
156
+ }
157
+ composeFrom(_agentCtx, _parentCtx) {
158
+ return undefined;
159
+ }
160
+ composedPreset(_agentCtx) {
161
+ return AGY_PRESET.id;
162
+ }
163
+ async standingKeyFor(id) {
164
+ await this.resolve(id);
165
+ return STANDING_KEY;
166
+ }
167
+ };
168
+ })();
169
+ export { SingleAgyPresetRoster };