@snack-kit/porygon 0.2.0 → 0.4.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/README.md CHANGED
@@ -551,6 +551,23 @@ npm run playground # 启动 Playground
551
551
 
552
552
  ## Changelog
553
553
 
554
+ ### v0.4.0
555
+
556
+ #### Bug Fixes
557
+
558
+ - **Claude adapter**: 修复 `result` 事件中 `inputTokens` 和 `outputTokens` 始终为 0 的问题。Claude CLI 的 token 统计位于 `event.usage.input_tokens` / `event.usage.output_tokens`,而非顶层的 `event.input_tokens` / `event.output_tokens`
559
+ - **Claude adapter**: 修复 `costUsd` 始终为 `undefined` 的问题。Claude CLI 使用 `total_cost_usd` 字段名,而非 `cost_usd`
560
+
561
+ ### v0.3.0
562
+
563
+ #### Bug Fixes
564
+
565
+ - **Claude adapter**: 将 prompt 从 CLI 参数 (`-p <prompt>`) 改为通过 stdin 传递 (`--print` + stdin pipe),修复超长 prompt 导致的偶发 403 "Request not allowed" 错误
566
+
567
+ #### 改进
568
+
569
+ - `SpawnOptions` 新增 `stdinData` 字段,`EphemeralProcess.executeStreaming` 支持向子进程 stdin 写入数据
570
+
554
571
  ### v0.2.0
555
572
 
556
573
  #### 新特性
package/dist/index.cjs CHANGED
@@ -28,6 +28,7 @@ __export(index_exports, {
28
28
  AgentTimeoutError: () => AgentTimeoutError,
29
29
  ClaudeAdapter: () => ClaudeAdapter,
30
30
  ConfigValidationError: () => ConfigValidationError,
31
+ InteractiveSession: () => InteractiveSession,
31
32
  InterceptorRejectedError: () => InterceptorRejectedError,
32
33
  OpenCodeAdapter: () => OpenCodeAdapter,
33
34
  Porygon: () => Porygon,
@@ -350,12 +351,18 @@ var EphemeralProcess = class {
350
351
  throw new Error("Process aborted before start");
351
352
  }
352
353
  this.aborted = false;
354
+ const useStdin = options.stdinData !== void 0;
353
355
  const child = (0, import_node_child_process.spawn)(options.command, options.args, {
354
356
  cwd: options.cwd,
355
357
  env: options.env ?? void 0,
356
- stdio: ["ignore", "pipe", "pipe"]
358
+ stdio: [useStdin ? "pipe" : "ignore", "pipe", "pipe"]
357
359
  });
358
360
  this.childProcess = child;
361
+ if (useStdin && child.stdin) {
362
+ child.stdin.write(options.stdinData, () => {
363
+ child.stdin.end();
364
+ });
365
+ }
359
366
  let timeoutTimer;
360
367
  if (options.timeoutMs !== void 0 && options.timeoutMs > 0) {
361
368
  timeoutTimer = setTimeout(() => {
@@ -852,14 +859,15 @@ function mapClaudeEvent(event, sessionId) {
852
859
  return [];
853
860
  }
854
861
  if (isResultEvent(event)) {
862
+ const usage = event.usage;
855
863
  return [{
856
864
  ...baseFields,
857
865
  type: "result",
858
866
  text: event.result,
859
- costUsd: event.cost_usd,
867
+ costUsd: event.total_cost_usd ?? event.cost_usd,
860
868
  durationMs: event.duration_ms,
861
- inputTokens: event.input_tokens,
862
- outputTokens: event.output_tokens
869
+ inputTokens: usage?.input_tokens ?? event.input_tokens,
870
+ outputTokens: usage?.output_tokens ?? event.output_tokens
863
871
  }];
864
872
  }
865
873
  return [];
@@ -967,7 +975,8 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
967
975
  "tool-restriction",
968
976
  "mcp",
969
977
  "subagents",
970
- "worktree"
978
+ "worktree",
979
+ "interactive-session"
971
980
  ]),
972
981
  streamingMode: "chunked",
973
982
  outputFormats: ["text", "json", "stream-json"],
@@ -1000,12 +1009,14 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
1000
1009
  if (v) cleanEnv[k] = v;
1001
1010
  }
1002
1011
  }
1012
+ const stdinData = this.buildStdinData(request);
1003
1013
  const streamOptions = {
1004
1014
  command: this.cliCommand,
1005
1015
  args,
1006
1016
  ...cwd ? { cwd } : {},
1007
1017
  env: cleanEnv,
1008
- timeoutMs: request.timeoutMs
1018
+ timeoutMs: request.timeoutMs,
1019
+ stdinData
1009
1020
  };
1010
1021
  const cmdStr = [this.cliCommand, ...args.map((a) => /[\s"']/.test(a) ? JSON.stringify(a) : a)].join(" ");
1011
1022
  const debugCmd = cwd ? `cd ${JSON.stringify(cwd)} && ${cmdStr}` : cmdStr;
@@ -1138,10 +1149,16 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
1138
1149
  env["NO_PROXY"] = noProxy;
1139
1150
  }
1140
1151
  }
1152
+ /**
1153
+ * 构建通过 stdin 传递给 Claude CLI 的数据。
1154
+ * 使用 stdin 而非 CLI 参数传递 prompt,避免超长参数导致的 403 错误。
1155
+ */
1156
+ buildStdinData(request) {
1157
+ return request.prompt;
1158
+ }
1141
1159
  buildArgs(request) {
1142
1160
  const args = [
1143
- "-p",
1144
- request.prompt,
1161
+ "--print",
1145
1162
  "--output-format",
1146
1163
  "stream-json",
1147
1164
  "--verbose"
@@ -1753,6 +1770,54 @@ var OpenCodeAdapter = class extends AbstractAgentAdapter {
1753
1770
  }
1754
1771
  };
1755
1772
 
1773
+ // src/session/interactive-session.ts
1774
+ var InteractiveSession = class {
1775
+ initialSessionId;
1776
+ resolvedSessionId;
1777
+ adapter;
1778
+ baseRequest;
1779
+ firstSent = false;
1780
+ closed = false;
1781
+ constructor(initialSessionId, adapter, baseRequest) {
1782
+ this.initialSessionId = initialSessionId;
1783
+ this.adapter = adapter;
1784
+ this.baseRequest = baseRequest;
1785
+ }
1786
+ /** 当前生效的 sessionId(首次 send 后反映 CLI 返回的真实 ID) */
1787
+ get sessionId() {
1788
+ return this.resolvedSessionId ?? this.initialSessionId;
1789
+ }
1790
+ /** 会话是否仍然活跃 */
1791
+ get isActive() {
1792
+ return !this.closed;
1793
+ }
1794
+ /**
1795
+ * 发送一条消息,返回流式响应。
1796
+ * 首次调用使用 initialSessionId,后续自动附加 resume。
1797
+ */
1798
+ async *send(prompt) {
1799
+ if (this.closed) {
1800
+ throw new SessionNotFoundError(this.sessionId);
1801
+ }
1802
+ const request = {
1803
+ ...this.baseRequest,
1804
+ prompt,
1805
+ ...this.firstSent ? { resume: this.sessionId } : {}
1806
+ };
1807
+ for await (const msg of this.adapter.query(request)) {
1808
+ if (!this.firstSent && msg.sessionId) {
1809
+ this.resolvedSessionId = msg.sessionId;
1810
+ }
1811
+ yield msg;
1812
+ }
1813
+ this.firstSent = true;
1814
+ }
1815
+ /** 关闭会话(仅清理内部状态,无进程需要释放) */
1816
+ close() {
1817
+ this.closed = true;
1818
+ }
1819
+ };
1820
+
1756
1821
  // src/porygon.ts
1757
1822
  var Porygon = class extends import_node_events2.EventEmitter {
1758
1823
  config;
@@ -1852,6 +1917,21 @@ var Porygon = class extends import_node_events2.EventEmitter {
1852
1917
  }
1853
1918
  return resultText;
1854
1919
  }
1920
+ /**
1921
+ * 创建交互式多轮对话会话。
1922
+ * 自动管理 sessionId 和 resume,对调用方透明。
1923
+ */
1924
+ session(options) {
1925
+ const backend = options?.backend ?? this.config.defaultBackend ?? "claude";
1926
+ const adapter = this.getAdapter(backend);
1927
+ const merged = this.mergeRequest({ ...options, prompt: "" }, backend);
1928
+ const { prompt: _, ...baseRequest } = merged;
1929
+ return new InteractiveSession(
1930
+ crypto.randomUUID(),
1931
+ adapter,
1932
+ baseRequest
1933
+ );
1934
+ }
1855
1935
  /**
1856
1936
  * 注册拦截器
1857
1937
  * @param direction 拦截方向
@@ -2100,6 +2180,7 @@ function createOutputGuard(options) {
2100
2180
  AgentTimeoutError,
2101
2181
  ClaudeAdapter,
2102
2182
  ConfigValidationError,
2183
+ InteractiveSession,
2103
2184
  InterceptorRejectedError,
2104
2185
  OpenCodeAdapter,
2105
2186
  Porygon,