Просмотр исходного кода

fix(stdio): close startup failure gaps

Tianyi Cui 2 месяцев назад
Родитель
Сommit
d56cc8d424

+ 20 - 11
packages/sdk/helper/src/templates/assets/index.ts.tpl

@@ -20,17 +20,26 @@ export async function main(boot: SdkBootContext) {
 {{/if}}
   const ctx = await startSDK(new URL('./cordis.yml', import.meta.url))
 {{#if isStdio}}
-  if (resume === undefined) {
-    await ctx.agents.create({
-      sessionId,
-      meta: { cwd: boot.cwd },
-      agentOptions: { model },
-    })
-  } else {
-    await ctx.agents.resume({
-      resumeSessionId: sessionId,
-      agentOptions: { model },
-    })
+  try {
+    if (resume === undefined) {
+      await ctx.agents.create({
+        sessionId,
+        meta: { cwd: boot.cwd },
+        agentOptions: { model },
+      })
+    } else {
+      await ctx.agents.resume({
+        resumeSessionId: sessionId,
+        agentOptions: { model },
+      })
+    }
+  } catch (error) {
+    try {
+      await ctx.fiber.dispose()
+    } catch (disposeError) {
+      throw new AggregateError([error, disposeError], 'stdio startup and cleanup failed')
+    }
+    throw error
   }
 {{else}}
 {{#if isEmbed}}

+ 2 - 0
packages/sdk/helper/tests/project.spec.ts

@@ -171,6 +171,8 @@ describe('SdkProject and ProjectEditSession', () => {
     expect(index).toContain('const sessionId = SessionId(resume ?? `main-session-${randomUUID()}`)')
     expect(index).toContain('process.env.DSH_SDK_SESSION_ID = sessionId')
     expect(index).toContain('resumeSessionId: sessionId')
+    expect(index).toContain('await ctx.fiber.dispose()')
+    expect(index).toContain("new AggregateError([error, disposeError], 'stdio startup and cleanup failed')")
     expect(project.packageManifest().scripts).toEqual({
       dev: 'dsh-sdk dev index.ts -- --model="deepseek-v4-flash"',
       build: 'dsh-sdk build',

+ 3 - 3
packages/ui/stdio/README.md

@@ -9,7 +9,7 @@ This package owns the terminal channel only. It injects `agents` and `userIntera
 | Key | Default | Meaning |
 |---|---|---|
 | `welcome` | `ready.` | Banner printed before the first prompt |
-| `agent` | `main` | Agent id driven by stdin and observed for EOF shutdown |
+| `sessionId` | `main` | Exact agent/session identity driven by stdin and observed for EOF shutdown |
 
 The plugin seeds display labels from the live agent registry, then tracks `agent/created` and `agent/disposed` so HMR and externally managed agents render consistently. Disposal closes readline and unregisters every listener/provider through Cordis effects.
 
@@ -18,7 +18,7 @@ The plugin seeds display labels from the live agent registry, then tracks `agent
   name: '@deepseek-ai/dsh-stdio'
   config:
     welcome: 'agent REPL ready. Give it a coding task.'
-    agent: main
+    sessionId: main
 ```
 
 ## Model Experience
@@ -37,6 +37,6 @@ The plugin seeds display labels from the live agent registry, then tracks `agent
 
 ## Known Limitations and Deferred Work
 
-- **One configured agent receives stdin** — the session/event renderer can print output from any session, but input lines always drive the configured `agent` id rather than routing by the visible label.
+- **One configured session receives stdin** — the session/event renderer can print output from any session, but input lines always drive the configured `sessionId` rather than routing by the visible label.
 - **Terminal questions are text-only and sequential** — the provider queues asks, supports option labels plus custom text, and has no richer UI shapes such as file pickers or diff previews.
 - **Closed stdin ends the terminal channel** — EOF rejects active or queued questions and exits after submitted work reaches idle; there is no reconnect path for a long-lived process.

+ 5 - 0
packages/ui/stdio/package.json

@@ -29,6 +29,11 @@
     "@deepseek-ai/dsh-user-interaction": "^0.0.1",
     "cordis": "^4.0.0-rc.7"
   },
+  "peerDependenciesMeta": {
+    "@deepseek-ai/dsh-agent-loop": {
+      "optional": true
+    }
+  },
   "dependencies": {
     "schemastery": "^3.18.0"
   },