super-dsh 0.1.9 → 0.1.10

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 (166) hide show
  1. package/README.md +25 -3
  2. package/agent-agy/cordis.patch.yml +7 -4
  3. package/agent-agy/dist/agent.d.ts +12 -11
  4. package/agent-agy/dist/agy-auth-gateway.d.ts +124 -0
  5. package/agent-agy/dist/agy-auth-gateway.js +233 -0
  6. package/agent-agy/dist/agy-auth.d.ts +153 -0
  7. package/agent-agy/dist/agy-auth.js +179 -0
  8. package/agent-agy/dist/agy-cli-client.d.ts +39 -9
  9. package/agent-agy/dist/agy-cli-client.js +82 -18
  10. package/agent-agy/dist/agy-store.d.ts +8 -0
  11. package/agent-agy/dist/index.d.ts +46 -16
  12. package/agent-agy/dist/index.js +82 -56
  13. package/agent-agy/dist/models.d.ts +70 -0
  14. package/agent-agy/dist/models.js +102 -3
  15. package/agent-agy/package.json +7 -5
  16. package/agent-agy-sdk/cordis.patch.yml +58 -0
  17. package/agent-agy-sdk/dist/adapter.d.ts +15 -0
  18. package/agent-agy-sdk/dist/adapter.js +42 -0
  19. package/agent-agy-sdk/dist/agent-preset-agy.d.ts +27 -0
  20. package/agent-agy-sdk/dist/agent-preset-agy.js +32 -0
  21. package/agent-agy-sdk/dist/agent.d.ts +138 -0
  22. package/agent-agy-sdk/dist/agent.js +494 -0
  23. package/{agent-agy → agent-agy-sdk}/dist/agy-client.d.ts +26 -6
  24. package/{agent-agy → agent-agy-sdk}/dist/agy-client.js +43 -25
  25. package/agent-agy-sdk/dist/agy-sessions.d.ts +25 -0
  26. package/agent-agy-sdk/dist/agy-sessions.js +107 -0
  27. package/agent-agy-sdk/dist/agy-store.d.ts +39 -0
  28. package/agent-agy-sdk/dist/agy-store.js +83 -0
  29. package/agent-agy-sdk/dist/inbox.d.ts +26 -0
  30. package/agent-agy-sdk/dist/inbox.js +53 -0
  31. package/agent-agy-sdk/dist/index.d.ts +93 -0
  32. package/agent-agy-sdk/dist/index.js +476 -0
  33. package/agent-agy-sdk/dist/models.d.ts +41 -0
  34. package/agent-agy-sdk/dist/models.js +74 -0
  35. package/agent-agy-sdk/dist/sdk-venv.d.ts +76 -0
  36. package/agent-agy-sdk/dist/sdk-venv.js +173 -0
  37. package/agent-agy-sdk/dist/world-plugin.d.ts +10 -0
  38. package/agent-agy-sdk/dist/world-plugin.js +82 -0
  39. package/agent-agy-sdk/package.json +64 -0
  40. package/agent-codex/README.md +141 -36
  41. package/agent-codex/cordis.patch.yml +14 -10
  42. package/agent-codex/dist/adapter.js +64 -28
  43. package/agent-codex/dist/agent.js +255 -81
  44. package/agent-codex/dist/app-server-approvals.js +165 -0
  45. package/agent-codex/dist/app-server-client.js +723 -0
  46. package/agent-codex/dist/app-server-events.js +432 -0
  47. package/agent-codex/dist/app-server-protocol.js +95 -0
  48. package/agent-codex/dist/app-server-wire.js +299 -0
  49. package/agent-codex/dist/codex-launch.js +78 -0
  50. package/agent-codex/dist/codex-native-config.js +392 -0
  51. package/agent-codex/dist/codex-provider-gateway.js +402 -0
  52. package/agent-codex/dist/codex-providers.js +376 -0
  53. package/agent-codex/dist/codex-route.js +86 -0
  54. package/agent-codex/dist/index.js +256 -115
  55. package/agent-codex/dist/permission.js +75 -1
  56. package/agent-codex/package.json +1 -2
  57. package/agent-codex-sdk/README.md +79 -0
  58. package/agent-codex-sdk/cordis.patch.yml +81 -0
  59. package/agent-codex-sdk/dist/adapter.js +91 -0
  60. package/agent-codex-sdk/dist/agent-preset-codex.js +32 -0
  61. package/agent-codex-sdk/dist/agent.js +1085 -0
  62. package/agent-codex-sdk/dist/codex-native-config.js +392 -0
  63. package/agent-codex-sdk/dist/codex-provider-gateway.js +402 -0
  64. package/agent-codex-sdk/dist/codex-providers.js +376 -0
  65. package/agent-codex-sdk/dist/codex-route.js +86 -0
  66. package/{agent-codex/dist/codex-client.js → agent-codex-sdk/dist/codex-sdk-client.js} +253 -32
  67. package/agent-codex-sdk/dist/codex-sdk-launch.js +92 -0
  68. package/agent-codex-sdk/dist/codex-store.js +180 -0
  69. package/agent-codex-sdk/dist/inbox.js +53 -0
  70. package/agent-codex-sdk/dist/index.js +739 -0
  71. package/agent-codex-sdk/dist/permission.js +71 -0
  72. package/agent-codex-sdk/dist/sdk-runtime.js +307 -0
  73. package/agent-codex-sdk/dist/session-map.js +167 -0
  74. package/agent-codex-sdk/dist/world-plugin.js +93 -0
  75. package/agent-codex-sdk/package.json +79 -0
  76. package/agent-codex-sdk/scripts/fetch-sdk-runtime.mjs +57 -0
  77. package/agent-omp/README.md +72 -55
  78. package/agent-omp/dist/agent.js +36 -11
  79. package/agent-omp/dist/index.js +92 -52
  80. package/agent-omp/dist/lazy-rpc.js +11 -4
  81. package/agent-omp/dist/omp-cli.js +114 -97
  82. package/agent-omp/dist/omp-store.js +12 -4
  83. package/agent-omp/dist/permission.js +70 -5
  84. package/agent-omp/dist/rpc-client.js +409 -0
  85. package/agent-omp/dist/world-plugin.js +36 -3
  86. package/agent-omp/package.json +2 -6
  87. package/agent-omp-sdk/README.md +103 -0
  88. package/agent-omp-sdk/cordis.patch.yml +108 -0
  89. package/agent-omp-sdk/dist/adapter.d.ts +21 -0
  90. package/agent-omp-sdk/dist/adapter.js +36 -0
  91. package/agent-omp-sdk/dist/agent-preset-omp.d.ts +31 -0
  92. package/agent-omp-sdk/dist/agent-preset-omp.js +32 -0
  93. package/agent-omp-sdk/dist/agent.d.ts +64 -0
  94. package/agent-omp-sdk/dist/agent.js +1178 -0
  95. package/agent-omp-sdk/dist/discovery.d.ts +57 -0
  96. package/agent-omp-sdk/dist/discovery.js +116 -0
  97. package/agent-omp-sdk/dist/inbox.d.ts +28 -0
  98. package/agent-omp-sdk/dist/inbox.js +53 -0
  99. package/agent-omp-sdk/dist/index.d.ts +60 -0
  100. package/agent-omp-sdk/dist/index.js +632 -0
  101. package/agent-omp-sdk/dist/knobs.d.ts +34 -0
  102. package/agent-omp-sdk/dist/knobs.js +50 -0
  103. package/agent-omp-sdk/dist/lazy-rpc.d.ts +98 -0
  104. package/agent-omp-sdk/dist/lazy-rpc.js +178 -0
  105. package/agent-omp-sdk/dist/mobile/zoom-guard.d.ts +187 -0
  106. package/agent-omp-sdk/dist/mobile/zoom-guard.js +294 -0
  107. package/agent-omp-sdk/dist/mobile-boot.d.ts +49 -0
  108. package/agent-omp-sdk/dist/mobile-boot.js +38 -0
  109. package/agent-omp-sdk/dist/models.d.ts +39 -0
  110. package/agent-omp-sdk/dist/models.js +299 -0
  111. package/agent-omp-sdk/dist/omp-cli.d.ts +67 -0
  112. package/agent-omp-sdk/dist/omp-cli.js +164 -0
  113. package/agent-omp-sdk/dist/omp-disk-discovery.d.ts +27 -0
  114. package/agent-omp-sdk/dist/omp-disk-discovery.js +178 -0
  115. package/agent-omp-sdk/dist/omp-executable.d.ts +2 -0
  116. package/agent-omp-sdk/dist/omp-executable.js +30 -0
  117. package/agent-omp-sdk/dist/omp-store.d.ts +134 -0
  118. package/agent-omp-sdk/dist/omp-store.js +526 -0
  119. package/agent-omp-sdk/dist/pairing.d.ts +7 -0
  120. package/agent-omp-sdk/dist/pairing.js +45 -0
  121. package/agent-omp-sdk/dist/permission.d.ts +138 -0
  122. package/agent-omp-sdk/dist/permission.js +161 -0
  123. package/agent-omp-sdk/dist/protocol.d.ts +189 -0
  124. package/agent-omp-sdk/dist/rpc-types.d.ts +77 -0
  125. package/agent-omp-sdk/dist/rpc-types.js +1 -0
  126. package/agent-omp-sdk/dist/sdk-client.d.ts +160 -0
  127. package/{agent-omp → agent-omp-sdk}/dist/sdk-client.js +108 -56
  128. package/agent-omp-sdk/dist/sdk-runtime.d.ts +211 -0
  129. package/agent-omp-sdk/dist/sdk-runtime.js +418 -0
  130. package/agent-omp-sdk/dist/sidecar-client.d.ts +87 -0
  131. package/agent-omp-sdk/dist/sidecar-client.js +197 -0
  132. package/agent-omp-sdk/dist/store/adapter.d.ts +17 -0
  133. package/agent-omp-sdk/dist/store/adapter.js +47 -0
  134. package/agent-omp-sdk/dist/store/db.d.ts +70 -0
  135. package/agent-omp-sdk/dist/store/db.js +210 -0
  136. package/agent-omp-sdk/dist/store/index.d.ts +23 -0
  137. package/agent-omp-sdk/dist/store/index.js +59 -0
  138. package/agent-omp-sdk/dist/store/reconcile.d.ts +15 -0
  139. package/agent-omp-sdk/dist/store/reconcile.js +51 -0
  140. package/agent-omp-sdk/dist/store/schema.d.ts +8 -0
  141. package/agent-omp-sdk/dist/store/schema.js +40 -0
  142. package/agent-omp-sdk/dist/transport.d.ts +49 -0
  143. package/agent-omp-sdk/dist/transport.js +35 -0
  144. package/agent-omp-sdk/dist/world-home.d.ts +4 -0
  145. package/agent-omp-sdk/dist/world-home.js +23 -0
  146. package/agent-omp-sdk/dist/world-plugin.d.ts +10 -0
  147. package/agent-omp-sdk/dist/world-plugin.js +137 -0
  148. package/agent-omp-sdk/package.json +87 -0
  149. package/client/index.js +1115 -93
  150. package/cordis.patch.yml +64 -0
  151. package/package.json +17 -7
  152. package/superd-hub/dist/client/agent-settings.d.ts +156 -0
  153. package/superd-hub/dist/client/agent-settings.js +229 -0
  154. package/superd-hub/dist/client/roster.js +6 -0
  155. package/superd-hub/dist/client/runtime-settings-api.d.ts +76 -0
  156. package/superd-hub/dist/client/runtime-settings-api.js +137 -0
  157. package/superd-hub/dist/client/switch.d.ts +165 -0
  158. package/superd-hub/dist/client/switch.js +295 -0
  159. package/superd-hub/lib/client/index.js +1115 -93
  160. package/agent-codex/dist/models.js +0 -152
  161. package/agent-omp/dist/sidecar-client.js +0 -195
  162. /package/{agent-agy → agent-agy-sdk}/bridge/agy_bridge.py +0 -0
  163. /package/{agent-agy → agent-agy-sdk}/bridge/gemini_openai_server.mjs +0 -0
  164. /package/{agent-codex/dist/codex-events.js → agent-codex-sdk/dist/codex-sdk-events.js} +0 -0
  165. /package/{agent-omp → agent-omp-sdk}/dist/protocol.js +0 -0
  166. /package/{agent-omp → agent-omp-sdk}/sidecar/main.ts +0 -0
@@ -0,0 +1,476 @@
1
+ /**
2
+ * agent-agy-sdk provider — Google Antigravity provider plugin for DeepSeek
3
+ * Harness (Super D, opt-in SDK line, 2026-10-02 split).
4
+ *
5
+ * The SDK half of the Antigravity split. An `AgentFactory` that drives an
6
+ * `AgyBridgeClient` (the Python google-antigravity SDK bridge child —
7
+ * localharness materializes inside it on the FIRST prompt, so create/resume
8
+ * spawn nothing). Auth is consumer-first: a Gemini API key persisted in the
9
+ * adapter's world-home state, or ADC via the environment (the SDK picks it up;
10
+ * this package NEVER invokes the agy CLI — dev-rules §16a). The CLI transport
11
+ * is the sibling package `@pgmi-builds/agent-agy`.
12
+ *
13
+ * Session identity ("mapping only"): the DSH session id is the authority,
14
+ * supplied by the harness. The SDK conversation id materializes after the
15
+ * first turn, so the pairing is persisted in `<worldHome>/dsh-sessions.json`
16
+ * (agy-sessions.ts). Resume resolves that record and fails CLOSED
17
+ * (CONVERSATION_NOT_FOUND) when no conversation is recorded. The DSH session
18
+ * log is the transcript authority, written through the upstream
19
+ * `sessionPersistence` service (hermes create/resume transaction shape).
20
+ *
21
+ * Deployment (2026-10-02 user ruling): the composite ships this row DISABLED
22
+ * and the package declares NO Python dependency and no postinstall — the
23
+ * durable venv under the world home is fetched lazily on first real use
24
+ * (sdk-venv.ts).
25
+ */
26
+ import { realpathSync, statSync } from "node:fs";
27
+ import { join } from "node:path";
28
+ import { Service } from "@deepseek-ai/cordis";
29
+ import { installModelSelection } from "@deepseek-ai/dsh-agent";
30
+ import { SessionLogOffset, SessionPreparation, interruptedTurnClosers, } from "@deepseek-ai/dsh-session";
31
+ import { AgyBridgeClient } from "./agy-client.js";
32
+ import { registerAntigravityPreset } from "./agent-preset-agy.js";
33
+ import { readAdapterState, writeAdapterState, readNativeDefaultModel, hasAdcEvidence, readGcloudProject } from "./agy-store.js";
34
+ import { AgyLlmAdapter, AGY_PROVIDER_ID } from "./adapter.js";
35
+ import { AgyAgent } from "./agent.js";
36
+ import { sessionRecord, upsertSession } from "./agy-sessions.js";
37
+ import { AGY_MODEL_CATALOG, parseModelId, toModelSlug } from "./models.js";
38
+ /** Realpath of a recorded cwd, accepted only when it names an existing directory. */
39
+ /** Extract the session-selected model id (string form) from agent options. */
40
+ function agentOptionsModel(agentOptions) {
41
+ const m = agentOptions?.model;
42
+ return typeof m === "string" && m !== "" ? m : undefined;
43
+ }
44
+ function validatedCwd(cwd) {
45
+ if (cwd === undefined)
46
+ return undefined;
47
+ try {
48
+ const canonical = realpathSync(cwd);
49
+ return statSync(canonical).isDirectory() ? canonical : undefined;
50
+ }
51
+ catch {
52
+ return undefined;
53
+ }
54
+ }
55
+ /** Diagnostic trace (AGY_TRACE=1 on the dsh process enables stderr tracing). */
56
+ const trace = (...parts) => {
57
+ if (process.env.AGY_TRACE === "1")
58
+ process.stderr.write(`[agy-sdk-provider ${Date.now() % 1_000_000}] ${parts.join(" ")}\n`);
59
+ };
60
+ /** Blank-session doctrine (§14 i): conversation content = turns actually ran. */
61
+ function hasConversationContent(events) {
62
+ return events.some((event) => {
63
+ if (event.type === "turn/start" || event.type === "assistant/message")
64
+ return true;
65
+ if (event.type === "user/message") {
66
+ const data = event.data;
67
+ return data?.source?.kind === "user";
68
+ }
69
+ return false;
70
+ });
71
+ }
72
+ /** Open a read handle, pull the complete stored log, close. */
73
+ async function readStoredEvents(persistence, id, signal) {
74
+ const handle = await persistence.open(id, "read", signal === undefined ? {} : { signal });
75
+ try {
76
+ return (await handle.read(0, undefined, signal === undefined ? {} : { signal })).events;
77
+ }
78
+ finally {
79
+ await handle.close();
80
+ }
81
+ }
82
+ export class AgyProvider extends Service {
83
+ static inject = ["agents", "sessions", "llm", "agentDefaultModel", "settings"];
84
+ /** Plain holder — prevents Cordis re-tracing the factory's ctx through a caller shadow. */
85
+ runtime;
86
+ /** The world's DSH home — anchors adapter-owned state (session map, api-key state). */
87
+ home;
88
+ opts;
89
+ defaultModelKey = "";
90
+ constructor(ctx, opts = {}) {
91
+ super(ctx, "agyProvider");
92
+ this.runtime = { ctx };
93
+ this.opts = opts;
94
+ this.home = this.resolveHome();
95
+ trace(`home: worldHome=${this.home}`);
96
+ ctx.effect(() => ctx.agents.setFactory(this), "agyProvider.setFactory()");
97
+ // The roster must be visible to the API gateway's root-level remote
98
+ // enumeration; register it on this plugin's own (top-level) fiber.
99
+ // 0.1.7: roster/selection/document RPC + the `agentPreset` session
100
+ // projection all live in the host AgentPresetRegistry (mounted by the
101
+ // dsh-web-app bundle's `agent-preset-registry` row; the adapter patch
102
+ // pins its `default` to the Antigravity preset). The adapter only registers
103
+ // its one fixed preset into the registry.
104
+ registerAntigravityPreset(ctx);
105
+ this.registerModelCatalog();
106
+ this.registerDefaultModel();
107
+ }
108
+ /** The world's DSH home: boot-provided dshHomePath first, env last. */
109
+ resolveHome() {
110
+ const fromBoot = this.runtime.ctx.get("dshHomePath");
111
+ if (typeof fromBoot === "string" && fromBoot !== "")
112
+ return fromBoot;
113
+ if (typeof fromBoot === "function") {
114
+ const resolved = fromBoot();
115
+ if (typeof resolved === "string" && resolved !== "")
116
+ return resolved;
117
+ }
118
+ return process.env.DSH_HOME ?? join(process.cwd(), ".tests", "superd");
119
+ }
120
+ /** Effective Gemini API key: adapter state first, then the environment. */
121
+ resolveApiKey() {
122
+ const state = readAdapterState(this.home);
123
+ if (typeof state.apiKey === "string" && state.apiKey !== "")
124
+ return state.apiKey;
125
+ const env = process.env.GEMINI_API_KEY;
126
+ return typeof env === "string" && env !== "" ? env : undefined;
127
+ }
128
+ /**
129
+ * Auth evidence for the §17 onboarding gate: an API key (state or env) OR
130
+ * ADC evidence is enough — the SDK consumes either transparently, so a user
131
+ * with gcloud ADC must NOT be pushed into key onboarding.
132
+ */
133
+ hasAuthEvidence() {
134
+ if (this.resolveApiKey() !== undefined)
135
+ return true;
136
+ return hasAdcEvidence(process.env, process.env.HOME ?? "");
137
+ }
138
+ /** Default model id: adapter state → native TUI default (slug-normalized) → catalog head. */
139
+ resolveDefaultModelId() {
140
+ const state = readAdapterState(this.home);
141
+ if (typeof state.defaultModel === "string" && parseModelId(state.defaultModel) !== undefined)
142
+ return state.defaultModel;
143
+ const native = readNativeDefaultModel(this.opts.nativeHome ?? join(process.env.HOME ?? "", ".gemini"));
144
+ const slug = toModelSlug(native);
145
+ if (slug !== undefined)
146
+ return slug;
147
+ return AGY_MODEL_CATALOG[0].slug;
148
+ }
149
+ /** Legacy slug-only default (default-model push keeps slug form). */
150
+ resolveDefaultModel() {
151
+ const state = readAdapterState(this.home);
152
+ if (typeof state.defaultModel === "string" && state.defaultModel !== "")
153
+ return state.defaultModel;
154
+ const native = readNativeDefaultModel(this.opts.nativeHome ?? join(process.env.HOME ?? "", ".gemini"));
155
+ const slug = toModelSlug(native);
156
+ if (slug !== undefined)
157
+ return slug;
158
+ return AGY_MODEL_CATALOG[0].slug;
159
+ }
160
+ /** Static route: provider `agy` + the resolved default (or the explicit ask).
161
+ * The selected model id (`slug` or `slug@level`) rides in `options.model`
162
+ * so clientOptions can forward slug+thinkingLevel to the bridge start frame. */
163
+ sessionAgentOptions(requested) {
164
+ const requestedModel = typeof requested?.model === "string" ? requested.model : undefined;
165
+ const explicit = requestedModel !== undefined && parseModelId(requestedModel) !== undefined
166
+ ? requestedModel
167
+ : undefined;
168
+ const model = explicit ?? this.resolveDefaultModelId();
169
+ return { ...(requested ?? {}), provider: AGY_PROVIDER_ID, model };
170
+ }
171
+ requirePersistence(op) {
172
+ const persistence = this.runtime.ctx.get("sessionPersistence");
173
+ if (persistence === undefined) {
174
+ throw new Error(`cannot ${op} an Agy session: session persistence is not configured`);
175
+ }
176
+ return persistence;
177
+ }
178
+ agentRuntime(cwd) {
179
+ const provider = this;
180
+ return {
181
+ session: { cwd },
182
+ awaitingKey: !provider.hasAuthEvidence(),
183
+ onSaveApiKey: (key) => {
184
+ writeAdapterState(provider.home, { ...readAdapterState(provider.home), apiKey: key });
185
+ trace("onboarding: api key persisted to adapter state (world home)");
186
+ },
187
+ onTurnDone: undefined, // replaced per-session by followConversationId below
188
+ };
189
+ }
190
+ /** Persist the Dash↔conversation pairing once the first turn materializes it. */
191
+ followConversationId = (id, client) => {
192
+ const cid = client.conversationId;
193
+ if (cid === "")
194
+ return;
195
+ try {
196
+ upsertSession(this.home, String(id), { conversationId: cid });
197
+ trace(`conversation id recorded for ${String(id)}: ${cid}`);
198
+ }
199
+ catch (error) {
200
+ this.runtime.ctx.logger.warn(`agy-sdk-provider: failed to persist the conversation id for ${String(id)}: ${String(error)}`);
201
+ }
202
+ };
203
+ registerModelCatalog() {
204
+ const llm = this.runtime.ctx.get("llm");
205
+ if (llm === undefined)
206
+ return;
207
+ llm.registerAdapter([AGY_PROVIDER_ID], new AgyLlmAdapter());
208
+ }
209
+ /** Push the resolved default model into the world's `agentDefaultModel`. */
210
+ registerDefaultModel() {
211
+ this.runtime.ctx.inject(["agentDefaultModel", "settings"], (mCtx) => {
212
+ const service = mCtx.get("agentDefaultModel");
213
+ if (service === undefined)
214
+ return;
215
+ const target = { provider: AGY_PROVIDER_ID, model: this.resolveDefaultModel() };
216
+ const targetKey = `${target.provider}/${target.model}`;
217
+ const current = service.currentSelection?.();
218
+ const currentKey = current === undefined ? undefined : `${current.provider}/${current.model}`;
219
+ if (currentKey === targetKey || currentKey === this.defaultModelKey)
220
+ return;
221
+ trace(`default model: ${currentKey ?? "none"} → ${targetKey}`);
222
+ const settings = mCtx.get("settings");
223
+ void Promise.resolve(settings?.replace?.("agent-default-model", target))
224
+ .then(() => service.saveSelection?.(target))
225
+ .then(() => {
226
+ this.defaultModelKey = targetKey;
227
+ this.runtime.ctx.logger.info(`agy-sdk-provider: default model ← static catalog / native settings: ${targetKey}`);
228
+ })
229
+ .catch((error) => {
230
+ this.runtime.ctx.logger.warn(`agy-sdk-provider: default model save failed: ${String(error)}`);
231
+ });
232
+ });
233
+ }
234
+ async createAgent(ownerCtx, options) {
235
+ const loopCtx = this.runtime.ctx;
236
+ const id = options.sessionId;
237
+ const meta = options.meta ?? {};
238
+ const cwd = meta.cwd;
239
+ if (meta.parentSession !== undefined) {
240
+ throw new Error(`cannot fork session "${meta.parentSession}" onto the Agy provider: Antigravity has no native session fork`);
241
+ }
242
+ const persistence = this.requirePersistence("create");
243
+ const spawnCwd = validatedCwd(cwd) ?? process.cwd();
244
+ trace(`create id=${id} spawnCwd=${spawnCwd}`);
245
+ // Full-lazy (§14-i): construct the client (spawns NOTHING); the bridge
246
+ // child materializes on the first real prompt.
247
+ const client = this.createLiveClient(spawnCwd, agentOptionsModel(options.agentOptions), options.agentOptions?.reasoningEffort);
248
+ const preparation = SessionPreparation.create(loopCtx.sessions.prepare(id, {
249
+ ...(options.seed === undefined ? {} : { seed: options.seed }),
250
+ ...(meta === undefined ? {} : { meta }),
251
+ ...(options.inheritedEventCount === undefined ? {} : { inheritedEventCount: options.inheritedEventCount }),
252
+ }));
253
+ let stored;
254
+ let handedOff = false;
255
+ try {
256
+ stored = await this.createStoredSession(persistence, preparation.session, options.signal);
257
+ upsertSession(this.home, String(id), { conversationId: null, cwd: spawnCwd, createdAt: Date.now(), preset: null });
258
+ handedOff = true;
259
+ const agentOptions = await this.sessionAgentOptions(options.agentOptions);
260
+ return await setupAndPublish(loopCtx, ownerCtx, id, agentOptions, options.setup, preparation.session, client, options.parentAgent, "startup", { enterSession: true, preparation }, stored, this.agentRuntime(spawnCwd), () => this.followConversationId(id, client));
261
+ }
262
+ catch (error) {
263
+ trace(`create ${id} THREW: ${String(error)}`);
264
+ void client.close();
265
+ if (!handedOff)
266
+ await stored?.handle.close().catch(() => { });
267
+ throw error;
268
+ }
269
+ finally {
270
+ if (!handedOff)
271
+ preparation[Symbol.dispose]();
272
+ }
273
+ }
274
+ async resume(ownerCtx, options) {
275
+ const loopCtx = this.runtime.ctx;
276
+ const id = options.resumeSessionId;
277
+ // Identity ("mapping only") with BLANK-SESSION TOLERANCE (2026-09-18,
278
+ // dev-rules §14 i — pi doctrine mirror): the Dash host eagerly resumes on
279
+ // view/model-change, so a never-materialized session (no record, or a
280
+ // record whose native conversation never produced a turn) resumes AS
281
+ // FRESH — zero processes until the first prompt. A MISSING record with
282
+ // conversation-bearing DSH log content stays a genuine orphan → fail loud.
283
+ const record = sessionRecord(this.home, String(id));
284
+ const blank = record === undefined || record.conversationId === null || record.conversationId === "";
285
+ trace(`resume id=${id} record=${record === undefined ? "MISSING" : record.conversationId ?? "NO-CONVERSATION"}${blank ? " (blank-tolerant)" : ""}`);
286
+ const persistence = this.requirePersistence("resume");
287
+ let orphanCheck;
288
+ if (record === undefined) {
289
+ orphanCheck = await readStoredEvents(persistence, id, options.signal);
290
+ if (hasConversationContent(orphanCheck)) {
291
+ const error = new Error(`cannot resume session "${id}": no Antigravity conversation is recorded for this Dash session id — start a new session`);
292
+ error.code = "CONVERSATION_NOT_FOUND";
293
+ throw error;
294
+ }
295
+ }
296
+ const spawnCwd = blank
297
+ ? (record !== undefined ? validatedCwd(record.cwd) ?? process.cwd() : process.cwd())
298
+ : (() => {
299
+ const verified = validatedCwd(record.cwd);
300
+ if (verified === undefined) {
301
+ throw new Error(`cannot resume session "${id}": its recorded working directory no longer exists`);
302
+ }
303
+ return verified;
304
+ })();
305
+ const client = this.createLiveClient(spawnCwd, agentOptionsModel(options.agentOptions), options.agentOptions?.reasoningEffort);
306
+ let handle;
307
+ let stored;
308
+ let preparation;
309
+ let handedOff = false;
310
+ try {
311
+ handle = await persistence.open(id, "write", options.signal === undefined ? {} : { signal: options.signal });
312
+ const cold = await handle.read(0, undefined, options.signal === undefined ? {} : { signal: options.signal });
313
+ const closers = interruptedTurnClosers(cold.events);
314
+ if (closers.length > 0)
315
+ await handle.append(closers);
316
+ preparation = SessionPreparation.create(loopCtx.sessions.prepare(id, {
317
+ seed: [...cold.events, ...closers],
318
+ meta: structuredClone(handle.header),
319
+ inheritedEventCount: handle.inheritedEventCount,
320
+ eventState: cold.eventState,
321
+ }));
322
+ stored = { handle, storedCount: cold.events.length + closers.length };
323
+ trace(`resume id=${id} seeded ${cold.events.length} events (+${closers.length} closers)`);
324
+ handle = undefined;
325
+ handedOff = true;
326
+ const agentOptions = await this.sessionAgentOptions(options.agentOptions);
327
+ return await setupAndPublish(loopCtx, ownerCtx, id, agentOptions, options.setup, preparation.session, client, options.parentAgent, "resume", { enterSession: true, preparation }, stored, this.agentRuntime(spawnCwd), () => this.followConversationId(id, client));
328
+ }
329
+ catch (error) {
330
+ trace(`resume ${id} THREW: ${String(error)}`);
331
+ void client.close();
332
+ if (!handedOff)
333
+ await handle?.close().catch(() => { });
334
+ throw error;
335
+ }
336
+ finally {
337
+ if (!handedOff)
338
+ preparation?.[Symbol.dispose]();
339
+ }
340
+ }
341
+ /**
342
+ * Live transport: SDK-ONLY (2026-10-02 user ruling).
343
+ *
344
+ * This package is the opt-in SDK half of the Antigravity split: it drives the
345
+ * Python google-antigravity SDK through the JSONL bridge and NEVER invokes the
346
+ * user's `agy` CLI (that transport is `@pgmi-builds/agent-agy`).
347
+ * Auth (§16a transparency): a key from adapter state / `GEMINI_API_KEY` rides
348
+ * the start frame; without one the SDK falls back to ADC (Vertex, project read
349
+ * from the gcloud default). Neither ⇒ the §17 onboarding branch answers the
350
+ * first turn locally, before anything spawns.
351
+ */
352
+ createLiveClient(cwd, modelId, reasoningEffort) {
353
+ return new AgyBridgeClient(this.clientOptions(cwd, modelId, reasoningEffort));
354
+ }
355
+ /** Bridge child options: world cwd + overrides; the api key rides the start config. */
356
+ clientOptions(cwd, modelId, reasoningEffort) {
357
+ // Auth plane (§16a): API key when present; otherwise the SDK's Vertex
358
+ // endpoint over ADC — which needs an explicit project, read from the
359
+ // gcloud CLI default. Without either, sessions gate into §17 onboarding.
360
+ const apiKey = this.resolveApiKey();
361
+ let project = this.opts.project;
362
+ let location = this.opts.location;
363
+ if (apiKey === undefined && project === undefined && hasAdcEvidence(process.env, process.env.HOME ?? "")) {
364
+ project = readGcloudProject(process.env.HOME ?? "");
365
+ location = location ?? "global";
366
+ }
367
+ // Session-selected model id wins over the provider default (previously a
368
+ // split-brain: the session showed one model, the bridge started another).
369
+ // `slug@level` decodes into model + thinking_level for the start frame.
370
+ const parsed = parseModelId(modelId ?? this.resolveDefaultModelId());
371
+ return {
372
+ apiKey,
373
+ project,
374
+ location,
375
+ model: parsed?.slug ?? this.resolveDefaultModelId(),
376
+ // TUI parity: the native default ships (Medium); without an explicit
377
+ // level the bridge sends none and thinking blocks rarely stream.
378
+ thinkingLevel: parsed?.thinkingLevel ?? "medium",
379
+ approvalMode: this.opts.approvalMode ?? "allow",
380
+ workspaces: [cwd],
381
+ bridgePath: this.opts.bridgePath,
382
+ pythonBin: this.opts.pythonBin,
383
+ spawnAttempts: this.opts.spawnAttempts,
384
+ // Anchors the durable SDK venv: per-world, writable, survives restarts.
385
+ worldHome: this.home,
386
+ };
387
+ }
388
+ async createStoredSession(persistence, session, signal) {
389
+ const handle = await persistence.create(session.header, {
390
+ inheritedEventCount: session.inheritedEventCount,
391
+ ...(signal === undefined ? {} : { signal }),
392
+ });
393
+ return { handle, storedCount: 0 };
394
+ }
395
+ }
396
+ /** Durable flush of the session's pre-publication suffix. */
397
+ async function appendUnstoredSuffix(stored, session) {
398
+ const suffix = session.snapshotEvents(SessionLogOffset(stored.storedCount));
399
+ if (suffix.length > 0)
400
+ await stored.handle.append(suffix);
401
+ stored.storedCount += suffix.length;
402
+ }
403
+ /** Shared create/resume transaction (module-level: Cordis tracing proxy). */
404
+ async function setupAndPublish(loopCtx, ownerCtx, id, agentOptions, setup, session, client, parentAgent, source, opts, stored, runtimeInfo, onTurnDone) {
405
+ const runtime = { ...runtimeInfo, onTurnDone };
406
+ let detachSession;
407
+ let detachAgent;
408
+ let agent;
409
+ let idleExit;
410
+ try {
411
+ agent = new AgyAgent(loopCtx, id, agentOptions, session, client, () => idleExit?.(), runtime);
412
+ // Pin THIS session to the agy route (static resolution — synchronous).
413
+ try {
414
+ const selection = { provider: agentOptions.provider ?? "agy", model: agentOptions.model ?? "" };
415
+ if (selection.model !== "") {
416
+ let picked = selection;
417
+ installModelSelection(agent.ctx, {
418
+ get current() { return picked; },
419
+ set current(next) { picked = next; },
420
+ assembled: undefined,
421
+ });
422
+ session.append("model/selection", selection);
423
+ trace(`session route pinned: ${selection.provider}/${selection.model}`);
424
+ }
425
+ }
426
+ catch (error) {
427
+ trace(`session route pin failed: ${String(error)}`);
428
+ }
429
+ const commit = await setup?.(agent.ctx, agent);
430
+ commit?.commit();
431
+ await appendUnstoredSuffix(stored, session);
432
+ if (opts.enterSession) {
433
+ detachSession = agent.ctx.sessions.enter(session);
434
+ agent.ctx.sessions.announce(session);
435
+ }
436
+ detachAgent = loopCtx.agents.enter(agent, parentAgent);
437
+ // 0.1.6: announce(agent, source) is required, async, and emits agent/created
438
+ // itself — the manual agent/session-start emission is gone upstream.
439
+ await loopCtx.agents.announce(agent, source);
440
+ let disposed = false;
441
+ let unfollowOwner;
442
+ const dispose = async () => {
443
+ if (disposed)
444
+ return;
445
+ disposed = true;
446
+ unfollowOwner?.();
447
+ await agent?.dispose();
448
+ detachAgent?.();
449
+ detachSession?.();
450
+ await stored.handle.close();
451
+ };
452
+ idleExit = () => {
453
+ void dispose().catch((error) => {
454
+ loopCtx.logger.warn(`agy-sdk-provider: teardown of ${id} failed: ${String(error)}`);
455
+ });
456
+ };
457
+ unfollowOwner = ownerCtx.effect(() => () => {
458
+ void dispose().catch((error) => {
459
+ loopCtx.logger.warn(`agy-sdk-provider: owner-unload teardown of ${id} failed: ${String(error)}`);
460
+ });
461
+ }, `agyProvider.lifecycle(${id})`);
462
+ return { agent, dispose };
463
+ }
464
+ catch (error) {
465
+ trace(`setupAndPublish ${id} FAILED: ${String(error)}`);
466
+ detachAgent?.();
467
+ detachSession?.();
468
+ void agent?.dispose().catch(() => { });
469
+ void client.close();
470
+ throw error;
471
+ }
472
+ finally {
473
+ opts.preparation?.[Symbol.dispose]();
474
+ }
475
+ }
476
+ export default AgyProvider;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Static model catalog for the Gemini consumer surface. The google-antigravity
3
+ * SDK has no model-list API and the CLI catalog path is auth-gated, so the
4
+ * catalog is static AND endpoint-verified (2026-09-18 probes on both auth
5
+ * planes; non-listed slugs 404 / are retired).
6
+ *
7
+ * Model ids are PLAIN slugs — thinking effort is a per-route option surfaced
8
+ * through the adapter's reasoning metadata (WebUI Effort selector →
9
+ * AgentOptions.reasoningEffort), NOT extra catalog entries (user ruling
10
+ * 2026-09-18).
11
+ */
12
+ export interface AgyCatalogModel {
13
+ slug: string;
14
+ label: string;
15
+ /** Effort levels the CLI/SDK accept for this slug (absent = no effort param). */
16
+ efforts?: readonly string[];
17
+ defaultEffort?: string;
18
+ }
19
+ export declare const AGY_MODEL_CATALOG: readonly AgyCatalogModel[];
20
+ /** Selectable model ids (plain slugs). */
21
+ export declare function modelIds(): string[];
22
+ export declare function catalogSlugs(): string[];
23
+ /** Parse a selectable model id: plain slug, or legacy `slug@level`. */
24
+ export declare function parseModelId(id: string | undefined): {
25
+ slug: string;
26
+ thinkingLevel?: string;
27
+ } | undefined;
28
+ /** Efforts for a slug (undefined = the slug takes no effort parameter). */
29
+ export declare function effortsFor(slug: string): readonly string[] | undefined;
30
+ export declare function defaultEffortFor(slug: string): string | undefined;
31
+ /** Cheapest live pair on the CLI key path (probed 2026-09-18). CLI default. */
32
+ export declare const CLI_DEFAULT_MODEL: {
33
+ slug: string;
34
+ thinkingLevel: string;
35
+ };
36
+ /**
37
+ * Normalize a model reference to an SDK-facing slug. The native TUI settings
38
+ * store display names ("Gemini 3.7 Flash (Medium)"); slugs pass through;
39
+ * unknown display names → undefined (caller uses the catalog head).
40
+ */
41
+ export declare function toModelSlug(model: string | undefined): string | undefined;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Static model catalog for the Gemini consumer surface. The google-antigravity
3
+ * SDK has no model-list API and the CLI catalog path is auth-gated, so the
4
+ * catalog is static AND endpoint-verified (2026-09-18 probes on both auth
5
+ * planes; non-listed slugs 404 / are retired).
6
+ *
7
+ * Model ids are PLAIN slugs — thinking effort is a per-route option surfaced
8
+ * through the adapter's reasoning metadata (WebUI Effort selector →
9
+ * AgentOptions.reasoningEffort), NOT extra catalog entries (user ruling
10
+ * 2026-09-18).
11
+ */
12
+ export const AGY_MODEL_CATALOG = [
13
+ { slug: "gemini-3.7-flash", label: "Gemini 3.7 Flash", efforts: ["low", "medium", "high"], defaultEffort: "medium" },
14
+ { slug: "gemini-3.6-flash", label: "Gemini 3.6 Flash", efforts: ["low", "medium", "high"], defaultEffort: "low" },
15
+ { slug: "gemini-3.5-flash", label: "Gemini 3.5 Flash", efforts: ["low", "medium", "high"], defaultEffort: "medium" },
16
+ { slug: "gemini-2.5-pro", label: "Gemini 2.5 Pro" },
17
+ { slug: "gemini-2.5-flash", label: "Gemini 2.5 Flash" },
18
+ ];
19
+ /** Selectable model ids (plain slugs). */
20
+ export function modelIds() {
21
+ return AGY_MODEL_CATALOG.map((m) => m.slug);
22
+ }
23
+ export function catalogSlugs() {
24
+ return modelIds();
25
+ }
26
+ /** Parse a selectable model id: plain slug, or legacy `slug@level`. */
27
+ export function parseModelId(id) {
28
+ if (id === undefined)
29
+ return undefined;
30
+ const at = id.indexOf("@");
31
+ const slug = at < 0 ? id : id.slice(0, at);
32
+ if (!catalogSlugs().includes(slug))
33
+ return undefined;
34
+ if (at < 0)
35
+ return { slug };
36
+ const level = id.slice(at + 1);
37
+ const entry = AGY_MODEL_CATALOG.find((m) => m.slug === slug);
38
+ if (entry?.efforts && entry.efforts.includes(level))
39
+ return { slug, thinkingLevel: level };
40
+ return { slug };
41
+ }
42
+ /** Efforts for a slug (undefined = the slug takes no effort parameter). */
43
+ export function effortsFor(slug) {
44
+ return AGY_MODEL_CATALOG.find((m) => m.slug === slug)?.efforts;
45
+ }
46
+ export function defaultEffortFor(slug) {
47
+ return AGY_MODEL_CATALOG.find((m) => m.slug === slug)?.defaultEffort;
48
+ }
49
+ /** Cheapest live pair on the CLI key path (probed 2026-09-18). CLI default. */
50
+ export const CLI_DEFAULT_MODEL = {
51
+ slug: "gemini-3.6-flash",
52
+ thinkingLevel: "low",
53
+ };
54
+ /**
55
+ * Normalize a model reference to an SDK-facing slug. The native TUI settings
56
+ * store display names ("Gemini 3.7 Flash (Medium)"); slugs pass through;
57
+ * unknown display names → undefined (caller uses the catalog head).
58
+ */
59
+ export function toModelSlug(model) {
60
+ if (!model)
61
+ return undefined;
62
+ const v = model.trim();
63
+ if (!v)
64
+ return undefined;
65
+ if (/^[a-z0-9.-]+$/.test(v) && !v.includes(" "))
66
+ return v;
67
+ const base = v.replace(/\s*\([^)]*\)\s*$/, "").trim().toLowerCase();
68
+ const slug = base.replace(/\s+/g, "-");
69
+ if (catalogSlugs().includes(slug))
70
+ return slug;
71
+ if (/^gemini-[0-9]/.test(slug))
72
+ return slug;
73
+ return undefined;
74
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * sdk-venv.ts — the lazy, durable Python venv the SDK bridge runs on.
3
+ *
4
+ * 2026-10-02 user ruling (spec §4.2): this package declares NO Python
5
+ * dependency and ships NO postinstall/allow-scripts hook. Nothing is fetched at
6
+ * install time; the FIRST real use fetches the venv into the world home, where
7
+ * it is durable across sessions, restarts and package upgrades. A second use
8
+ * reuses it (idempotent), and a FAILED fetch leaves a trace on disk instead of
9
+ * silently degrading into a confusing bridge crash.
10
+ *
11
+ * Why the world home rather than the package root: the package root is the
12
+ * installed tarball — read-only in every real consumer install, and shared by
13
+ * every world (the CLI one included). The world home is per-runtime, writable,
14
+ * and already the home of this adapter's own state.
15
+ *
16
+ * The bootstrap is two idempotent steps, so a half-built venv heals in place
17
+ * rather than being thrown away and re-downloaded:
18
+ * 1. `<worldHome>/sdk-venv/bin/python` missing → `python3 -m venv <dir>`
19
+ * 2. `<worldHome>/sdk-venv.json` marker missing → `pip install google-antigravity`
20
+ */
21
+ /** The Python distribution the bridge imports (`bridge/agy_bridge.py`). */
22
+ export declare const SDK_PYTHON_PACKAGE = "google-antigravity";
23
+ /** Layout of the durable venv inside a world home. */
24
+ export interface SdkVenvLayout {
25
+ /** The venv root. */
26
+ dir: string;
27
+ /** The interpreter the bridge is spawned with. */
28
+ python: string;
29
+ /** Used for the (idempotent) install step. */
30
+ pip: string;
31
+ /** Written last; its presence means the venv is complete. */
32
+ marker: string;
33
+ /** Failure trace — only ever written by a failed fetch. */
34
+ errorLog: string;
35
+ /** Bootstrap lock directory (mkdir is atomic on POSIX). */
36
+ lock: string;
37
+ }
38
+ export declare function sdkVenvLayout(worldHome: string): SdkVenvLayout;
39
+ export interface SdkVenvRecord {
40
+ /** Installed distribution name. */
41
+ package: string;
42
+ /** Interpreter path (absolute) at install time. */
43
+ python: string;
44
+ /** ISO timestamp of the successful fetch. */
45
+ installedAt: string;
46
+ /** Interpreter used to create the venv. */
47
+ createdWith: string;
48
+ }
49
+ /** Read the install marker; undefined when absent or unreadable. */
50
+ export declare function readSdkVenvRecord(worldHome: string): SdkVenvRecord | undefined;
51
+ /** Fast path: the complete venv already exists → the interpreter to use. */
52
+ export declare function resolveSdkPython(worldHome: string): string | undefined;
53
+ export interface EnsureSdkVenvOptions {
54
+ /** Interpreter used to CREATE the venv (default `python3`). */
55
+ basePython?: string;
56
+ /** Command runner (tests inject a fake; default is a synchronous exec). */
57
+ run?: (cmd: string, args: string[]) => void;
58
+ trace?: (...parts: unknown[]) => void;
59
+ /** How long to wait for another process's bootstrap before giving up (ms). */
60
+ lockTimeoutMs?: number;
61
+ /** Poll interval while another process holds the lock (ms). */
62
+ lockPollMs?: number;
63
+ /** Lock older than this is treated as abandoned and stolen (ms). */
64
+ lockStaleMs?: number;
65
+ now?: () => number;
66
+ sleep?: (ms: number) => Promise<void>;
67
+ }
68
+ /**
69
+ * Return the interpreter path for `<worldHome>`'s durable venv, fetching it
70
+ * first when it is absent or incomplete.
71
+ *
72
+ * Idempotent: a complete venv short-circuits without touching the network.
73
+ * Failures append to `<worldHome>/sdk-venv.error.log` and throw a single
74
+ * actionable error — never a partial venv left behind a success marker.
75
+ */
76
+ export declare function ensureSdkVenv(worldHome: string, opts?: EnsureSdkVenvOptions): Promise<string>;