@snack-kit/porygon 0.2.0 → 0.3.0

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.
package/dist/index.d.cts CHANGED
@@ -128,7 +128,7 @@ interface AgentErrorMessage extends BaseAgentMessage {
128
128
  * 适配器能力声明
129
129
  */
130
130
  interface AdapterCapabilities {
131
- features: Set<"streaming" | "session-resume" | "system-prompt" | "tool-restriction" | "mcp" | "subagents" | "worktree" | "budget-limit" | "serve-mode">;
131
+ features: Set<"streaming" | "session-resume" | "system-prompt" | "tool-restriction" | "mcp" | "subagents" | "worktree" | "budget-limit" | "serve-mode" | "interactive-session">;
132
132
  /**
133
133
  * 流式输出模式:
134
134
  * - 'delta': 后端原生产生增量 stream_chunk 事件(如 OpenCode)
@@ -209,6 +209,38 @@ interface InterceptorContext {
209
209
  */
210
210
  type InterceptorFn = (text: string, context: InterceptorContext) => string | boolean | undefined | Promise<string | boolean | undefined>;
211
211
 
212
+ /**
213
+ * 便捷的多轮对话封装。
214
+ *
215
+ * 底层仍是 per-turn spawn + --resume,但对调用方透明:
216
+ * ```ts
217
+ * const session = porygon.session({ systemPrompt: '...' });
218
+ * for await (const msg of session.send('Hello')) { ... }
219
+ * for await (const msg of session.send('继续')) { ... } // 自动 resume
220
+ * session.close();
221
+ * ```
222
+ */
223
+ declare class InteractiveSession {
224
+ readonly initialSessionId: string;
225
+ private resolvedSessionId?;
226
+ private adapter;
227
+ private baseRequest;
228
+ private firstSent;
229
+ private closed;
230
+ constructor(initialSessionId: string, adapter: IAgentAdapter, baseRequest: Omit<PromptRequest, "prompt">);
231
+ /** 当前生效的 sessionId(首次 send 后反映 CLI 返回的真实 ID) */
232
+ get sessionId(): string;
233
+ /** 会话是否仍然活跃 */
234
+ get isActive(): boolean;
235
+ /**
236
+ * 发送一条消息,返回流式响应。
237
+ * 首次调用使用 initialSessionId,后续自动附加 resume。
238
+ */
239
+ send(prompt: string): AsyncGenerator<AgentMessage>;
240
+ /** 关闭会话(仅清理内部状态,无进程需要释放) */
241
+ close(): void;
242
+ }
243
+
212
244
  /**
213
245
  * 健康检查结果(扁平化结构)
214
246
  */
@@ -270,6 +302,11 @@ declare class Porygon extends EventEmitter {
270
302
  * @returns 最终结果文本
271
303
  */
272
304
  run(request: PromptRequest): Promise<string>;
305
+ /**
306
+ * 创建交互式多轮对话会话。
307
+ * 自动管理 sessionId 和 resume,对调用方透明。
308
+ */
309
+ session(options?: Omit<PromptRequest, "prompt">): InteractiveSession;
273
310
  /**
274
311
  * 注册拦截器
275
312
  * @param direction 拦截方向
@@ -458,6 +495,8 @@ interface SpawnOptions {
458
495
  cwd?: string;
459
496
  env?: Record<string, string>;
460
497
  timeoutMs?: number;
498
+ /** 写入 stdin 后自动关闭的数据 */
499
+ stdinData?: string;
461
500
  }
462
501
  /**
463
502
  * 进程执行结果
@@ -681,6 +720,11 @@ declare class ClaudeAdapter extends AbstractAgentAdapter {
681
720
  * @param noProxy 不走代理的地址列表
682
721
  */
683
722
  private applyProxyEnv;
723
+ /**
724
+ * 构建通过 stdin 传递给 Claude CLI 的数据。
725
+ * 使用 stdin 而非 CLI 参数传递 prompt,避免超长参数导致的 403 错误。
726
+ */
727
+ private buildStdinData;
684
728
  private buildArgs;
685
729
  }
686
730
 
@@ -765,4 +809,4 @@ declare class OpenCodeAdapter extends AbstractAgentAdapter {
765
809
  private getConfigPath;
766
810
  }
767
811
 
768
- export { AbstractAgentAdapter, type AdapterCapabilities, AdapterIncompatibleError, AdapterNotAvailableError, AdapterNotFoundError, type AgentAssistantMessage, type AgentErrorMessage, AgentExecutionError, type AgentMessage, type AgentResultMessage, type AgentStreamChunkMessage, type AgentSystemMessage, AgentTimeoutError, type AgentToolUseMessage, type BackendConfig, ClaudeAdapter, type CompatibilityResult, ConfigValidationError, type GuardAction, type GuardOptions, type HealthCheckResult, type IAgentAdapter, type InterceptorContext, type InterceptorDirection, type InterceptorFn, InterceptorRejectedError, type McpServerConfig, type ModelInfo, OpenCodeAdapter, Porygon, type PorygonConfig, PorygonError, type PorygonEvents, type PromptRequest, type ProxyConfig, type SessionInfo, type SessionListOptions, SessionNotFoundError, createInputGuard, createOutputGuard, createPorygon };
812
+ export { AbstractAgentAdapter, type AdapterCapabilities, AdapterIncompatibleError, AdapterNotAvailableError, AdapterNotFoundError, type AgentAssistantMessage, type AgentErrorMessage, AgentExecutionError, type AgentMessage, type AgentResultMessage, type AgentStreamChunkMessage, type AgentSystemMessage, AgentTimeoutError, type AgentToolUseMessage, type BackendConfig, ClaudeAdapter, type CompatibilityResult, ConfigValidationError, type GuardAction, type GuardOptions, type HealthCheckResult, type IAgentAdapter, InteractiveSession, type InterceptorContext, type InterceptorDirection, type InterceptorFn, InterceptorRejectedError, type McpServerConfig, type ModelInfo, OpenCodeAdapter, Porygon, type PorygonConfig, PorygonError, type PorygonEvents, type PromptRequest, type ProxyConfig, type SessionInfo, type SessionListOptions, SessionNotFoundError, createInputGuard, createOutputGuard, createPorygon };
package/dist/index.d.ts CHANGED
@@ -128,7 +128,7 @@ interface AgentErrorMessage extends BaseAgentMessage {
128
128
  * 适配器能力声明
129
129
  */
130
130
  interface AdapterCapabilities {
131
- features: Set<"streaming" | "session-resume" | "system-prompt" | "tool-restriction" | "mcp" | "subagents" | "worktree" | "budget-limit" | "serve-mode">;
131
+ features: Set<"streaming" | "session-resume" | "system-prompt" | "tool-restriction" | "mcp" | "subagents" | "worktree" | "budget-limit" | "serve-mode" | "interactive-session">;
132
132
  /**
133
133
  * 流式输出模式:
134
134
  * - 'delta': 后端原生产生增量 stream_chunk 事件(如 OpenCode)
@@ -209,6 +209,38 @@ interface InterceptorContext {
209
209
  */
210
210
  type InterceptorFn = (text: string, context: InterceptorContext) => string | boolean | undefined | Promise<string | boolean | undefined>;
211
211
 
212
+ /**
213
+ * 便捷的多轮对话封装。
214
+ *
215
+ * 底层仍是 per-turn spawn + --resume,但对调用方透明:
216
+ * ```ts
217
+ * const session = porygon.session({ systemPrompt: '...' });
218
+ * for await (const msg of session.send('Hello')) { ... }
219
+ * for await (const msg of session.send('继续')) { ... } // 自动 resume
220
+ * session.close();
221
+ * ```
222
+ */
223
+ declare class InteractiveSession {
224
+ readonly initialSessionId: string;
225
+ private resolvedSessionId?;
226
+ private adapter;
227
+ private baseRequest;
228
+ private firstSent;
229
+ private closed;
230
+ constructor(initialSessionId: string, adapter: IAgentAdapter, baseRequest: Omit<PromptRequest, "prompt">);
231
+ /** 当前生效的 sessionId(首次 send 后反映 CLI 返回的真实 ID) */
232
+ get sessionId(): string;
233
+ /** 会话是否仍然活跃 */
234
+ get isActive(): boolean;
235
+ /**
236
+ * 发送一条消息,返回流式响应。
237
+ * 首次调用使用 initialSessionId,后续自动附加 resume。
238
+ */
239
+ send(prompt: string): AsyncGenerator<AgentMessage>;
240
+ /** 关闭会话(仅清理内部状态,无进程需要释放) */
241
+ close(): void;
242
+ }
243
+
212
244
  /**
213
245
  * 健康检查结果(扁平化结构)
214
246
  */
@@ -270,6 +302,11 @@ declare class Porygon extends EventEmitter {
270
302
  * @returns 最终结果文本
271
303
  */
272
304
  run(request: PromptRequest): Promise<string>;
305
+ /**
306
+ * 创建交互式多轮对话会话。
307
+ * 自动管理 sessionId 和 resume,对调用方透明。
308
+ */
309
+ session(options?: Omit<PromptRequest, "prompt">): InteractiveSession;
273
310
  /**
274
311
  * 注册拦截器
275
312
  * @param direction 拦截方向
@@ -458,6 +495,8 @@ interface SpawnOptions {
458
495
  cwd?: string;
459
496
  env?: Record<string, string>;
460
497
  timeoutMs?: number;
498
+ /** 写入 stdin 后自动关闭的数据 */
499
+ stdinData?: string;
461
500
  }
462
501
  /**
463
502
  * 进程执行结果
@@ -681,6 +720,11 @@ declare class ClaudeAdapter extends AbstractAgentAdapter {
681
720
  * @param noProxy 不走代理的地址列表
682
721
  */
683
722
  private applyProxyEnv;
723
+ /**
724
+ * 构建通过 stdin 传递给 Claude CLI 的数据。
725
+ * 使用 stdin 而非 CLI 参数传递 prompt,避免超长参数导致的 403 错误。
726
+ */
727
+ private buildStdinData;
684
728
  private buildArgs;
685
729
  }
686
730
 
@@ -765,4 +809,4 @@ declare class OpenCodeAdapter extends AbstractAgentAdapter {
765
809
  private getConfigPath;
766
810
  }
767
811
 
768
- export { AbstractAgentAdapter, type AdapterCapabilities, AdapterIncompatibleError, AdapterNotAvailableError, AdapterNotFoundError, type AgentAssistantMessage, type AgentErrorMessage, AgentExecutionError, type AgentMessage, type AgentResultMessage, type AgentStreamChunkMessage, type AgentSystemMessage, AgentTimeoutError, type AgentToolUseMessage, type BackendConfig, ClaudeAdapter, type CompatibilityResult, ConfigValidationError, type GuardAction, type GuardOptions, type HealthCheckResult, type IAgentAdapter, type InterceptorContext, type InterceptorDirection, type InterceptorFn, InterceptorRejectedError, type McpServerConfig, type ModelInfo, OpenCodeAdapter, Porygon, type PorygonConfig, PorygonError, type PorygonEvents, type PromptRequest, type ProxyConfig, type SessionInfo, type SessionListOptions, SessionNotFoundError, createInputGuard, createOutputGuard, createPorygon };
812
+ export { AbstractAgentAdapter, type AdapterCapabilities, AdapterIncompatibleError, AdapterNotAvailableError, AdapterNotFoundError, type AgentAssistantMessage, type AgentErrorMessage, AgentExecutionError, type AgentMessage, type AgentResultMessage, type AgentStreamChunkMessage, type AgentSystemMessage, AgentTimeoutError, type AgentToolUseMessage, type BackendConfig, ClaudeAdapter, type CompatibilityResult, ConfigValidationError, type GuardAction, type GuardOptions, type HealthCheckResult, type IAgentAdapter, InteractiveSession, type InterceptorContext, type InterceptorDirection, type InterceptorFn, InterceptorRejectedError, type McpServerConfig, type ModelInfo, OpenCodeAdapter, Porygon, type PorygonConfig, PorygonError, type PorygonEvents, type PromptRequest, type ProxyConfig, type SessionInfo, type SessionListOptions, SessionNotFoundError, createInputGuard, createOutputGuard, createPorygon };
package/dist/index.js CHANGED
@@ -309,12 +309,18 @@ var EphemeralProcess = class {
309
309
  throw new Error("Process aborted before start");
310
310
  }
311
311
  this.aborted = false;
312
+ const useStdin = options.stdinData !== void 0;
312
313
  const child = spawn(options.command, options.args, {
313
314
  cwd: options.cwd,
314
315
  env: options.env ?? void 0,
315
- stdio: ["ignore", "pipe", "pipe"]
316
+ stdio: [useStdin ? "pipe" : "ignore", "pipe", "pipe"]
316
317
  });
317
318
  this.childProcess = child;
319
+ if (useStdin && child.stdin) {
320
+ child.stdin.write(options.stdinData, () => {
321
+ child.stdin.end();
322
+ });
323
+ }
318
324
  let timeoutTimer;
319
325
  if (options.timeoutMs !== void 0 && options.timeoutMs > 0) {
320
326
  timeoutTimer = setTimeout(() => {
@@ -926,7 +932,8 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
926
932
  "tool-restriction",
927
933
  "mcp",
928
934
  "subagents",
929
- "worktree"
935
+ "worktree",
936
+ "interactive-session"
930
937
  ]),
931
938
  streamingMode: "chunked",
932
939
  outputFormats: ["text", "json", "stream-json"],
@@ -959,12 +966,14 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
959
966
  if (v) cleanEnv[k] = v;
960
967
  }
961
968
  }
969
+ const stdinData = this.buildStdinData(request);
962
970
  const streamOptions = {
963
971
  command: this.cliCommand,
964
972
  args,
965
973
  ...cwd ? { cwd } : {},
966
974
  env: cleanEnv,
967
- timeoutMs: request.timeoutMs
975
+ timeoutMs: request.timeoutMs,
976
+ stdinData
968
977
  };
969
978
  const cmdStr = [this.cliCommand, ...args.map((a) => /[\s"']/.test(a) ? JSON.stringify(a) : a)].join(" ");
970
979
  const debugCmd = cwd ? `cd ${JSON.stringify(cwd)} && ${cmdStr}` : cmdStr;
@@ -1097,10 +1106,16 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
1097
1106
  env["NO_PROXY"] = noProxy;
1098
1107
  }
1099
1108
  }
1109
+ /**
1110
+ * 构建通过 stdin 传递给 Claude CLI 的数据。
1111
+ * 使用 stdin 而非 CLI 参数传递 prompt,避免超长参数导致的 403 错误。
1112
+ */
1113
+ buildStdinData(request) {
1114
+ return request.prompt;
1115
+ }
1100
1116
  buildArgs(request) {
1101
1117
  const args = [
1102
- "-p",
1103
- request.prompt,
1118
+ "--print",
1104
1119
  "--output-format",
1105
1120
  "stream-json",
1106
1121
  "--verbose"
@@ -1712,6 +1727,54 @@ var OpenCodeAdapter = class extends AbstractAgentAdapter {
1712
1727
  }
1713
1728
  };
1714
1729
 
1730
+ // src/session/interactive-session.ts
1731
+ var InteractiveSession = class {
1732
+ initialSessionId;
1733
+ resolvedSessionId;
1734
+ adapter;
1735
+ baseRequest;
1736
+ firstSent = false;
1737
+ closed = false;
1738
+ constructor(initialSessionId, adapter, baseRequest) {
1739
+ this.initialSessionId = initialSessionId;
1740
+ this.adapter = adapter;
1741
+ this.baseRequest = baseRequest;
1742
+ }
1743
+ /** 当前生效的 sessionId(首次 send 后反映 CLI 返回的真实 ID) */
1744
+ get sessionId() {
1745
+ return this.resolvedSessionId ?? this.initialSessionId;
1746
+ }
1747
+ /** 会话是否仍然活跃 */
1748
+ get isActive() {
1749
+ return !this.closed;
1750
+ }
1751
+ /**
1752
+ * 发送一条消息,返回流式响应。
1753
+ * 首次调用使用 initialSessionId,后续自动附加 resume。
1754
+ */
1755
+ async *send(prompt) {
1756
+ if (this.closed) {
1757
+ throw new SessionNotFoundError(this.sessionId);
1758
+ }
1759
+ const request = {
1760
+ ...this.baseRequest,
1761
+ prompt,
1762
+ ...this.firstSent ? { resume: this.sessionId } : {}
1763
+ };
1764
+ for await (const msg of this.adapter.query(request)) {
1765
+ if (!this.firstSent && msg.sessionId) {
1766
+ this.resolvedSessionId = msg.sessionId;
1767
+ }
1768
+ yield msg;
1769
+ }
1770
+ this.firstSent = true;
1771
+ }
1772
+ /** 关闭会话(仅清理内部状态,无进程需要释放) */
1773
+ close() {
1774
+ this.closed = true;
1775
+ }
1776
+ };
1777
+
1715
1778
  // src/porygon.ts
1716
1779
  var Porygon = class extends EventEmitter2 {
1717
1780
  config;
@@ -1811,6 +1874,21 @@ var Porygon = class extends EventEmitter2 {
1811
1874
  }
1812
1875
  return resultText;
1813
1876
  }
1877
+ /**
1878
+ * 创建交互式多轮对话会话。
1879
+ * 自动管理 sessionId 和 resume,对调用方透明。
1880
+ */
1881
+ session(options) {
1882
+ const backend = options?.backend ?? this.config.defaultBackend ?? "claude";
1883
+ const adapter = this.getAdapter(backend);
1884
+ const merged = this.mergeRequest({ ...options, prompt: "" }, backend);
1885
+ const { prompt: _, ...baseRequest } = merged;
1886
+ return new InteractiveSession(
1887
+ crypto.randomUUID(),
1888
+ adapter,
1889
+ baseRequest
1890
+ );
1891
+ }
1814
1892
  /**
1815
1893
  * 注册拦截器
1816
1894
  * @param direction 拦截方向
@@ -2058,6 +2136,7 @@ export {
2058
2136
  AgentTimeoutError,
2059
2137
  ClaudeAdapter,
2060
2138
  ConfigValidationError,
2139
+ InteractiveSession,
2061
2140
  InterceptorRejectedError,
2062
2141
  OpenCodeAdapter,
2063
2142
  Porygon,