@downcity/agent 1.1.205 → 1.1.207

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.
@@ -1 +1 @@
1
- {"version":3,"file":"SessionTitle.d.ts","sourceRoot":"","sources":["../../src/session/SessionTitle.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,IAAI,CAAC;AACtD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AACnF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAE1E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AASvD;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,QAAQ,EAAE,eAAe,EAAE,CAAC;IAE5B;;OAEG;IACH,KAAK,CAAC,EAAE,aAAa,CAAC;IAEtB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAwLD;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,wBAAwB,GAC9B,OAAO,CAAC,oBAAoB,CAAC,CA2C/B"}
1
+ {"version":3,"file":"SessionTitle.d.ts","sourceRoot":"","sources":["../../src/session/SessionTitle.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,IAAI,CAAC;AAEpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AACnF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAE1E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AASvD;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,QAAQ,EAAE,eAAe,EAAE,CAAC;IAE5B;;OAEG;IACH,KAAK,CAAC,EAAE,aAAa,CAAC;IAEtB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA+LD;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,wBAAwB,GAC9B,OAAO,CAAC,oBAAoB,CAAC,CA2C/B"}
@@ -6,7 +6,8 @@
6
6
  * - title 默认允许为空;只有模型成功生成标题时才会写入。
7
7
  * - 当 title 仍为空时,后续执行链路可以再次尝试生成。
8
8
  */
9
- import { generateText } from "ai";
9
+ import { streamText } from "ai";
10
+ import { buildOpenAIResponsesProviderOptions } from "../executor/messages/SessionMessageCodec.js";
10
11
  import { is_session_message_record } from "../executor/types/SessionRecords.js";
11
12
  import { normalizeSessionTitle, readSessionMetadata, writeSessionMetadata, } from "../session/storage/Metadata.js";
12
13
  const GENERATED_SESSION_TITLE_MAX_CHARS = 24;
@@ -86,8 +87,9 @@ async function logSessionTitleDiagnostic(input) {
86
87
  }
87
88
  }
88
89
  async function generateSessionTitle(input) {
90
+ let observedStreamError;
89
91
  try {
90
- const result = await generateText({
92
+ const result = streamText({
91
93
  model: input.model,
92
94
  system: "You generate minimal conversation titles. Output only the title itself, with no explanation and no quotation marks.",
93
95
  prompt: [
@@ -96,8 +98,13 @@ async function generateSessionTitle(input) {
96
98
  "",
97
99
  input.firstUserText,
98
100
  ].join("\n"),
101
+ providerOptions: buildOpenAIResponsesProviderOptions(),
102
+ onError: ({ error }) => {
103
+ observedStreamError = error;
104
+ },
99
105
  });
100
- const generatedTitle = normalizeGeneratedTitle(result.text);
106
+ const text = await result.text;
107
+ const generatedTitle = normalizeGeneratedTitle(text);
101
108
  if (!generatedTitle) {
102
109
  await logSessionTitleDiagnostic({
103
110
  logger: input.logger,
@@ -107,13 +114,14 @@ async function generateSessionTitle(input) {
107
114
  details: {
108
115
  modelLabel: input.modelLabel || null,
109
116
  firstUserTextLength: input.firstUserText.length,
110
- rawTitleLength: String(result.text || "").length,
117
+ rawTitleLength: String(text || "").length,
111
118
  },
112
119
  });
113
120
  }
114
121
  return generatedTitle;
115
122
  }
116
123
  catch (error) {
124
+ const effectiveError = observedStreamError || error;
117
125
  await logSessionTitleDiagnostic({
118
126
  logger: input.logger,
119
127
  sessionId: input.sessionId,
@@ -122,7 +130,7 @@ async function generateSessionTitle(input) {
122
130
  details: {
123
131
  modelLabel: input.modelLabel || null,
124
132
  firstUserTextLength: input.firstUserText.length,
125
- ...summarizeTitleError(error),
133
+ ...summarizeTitleError(effectiveError),
126
134
  },
127
135
  });
128
136
  // 关键点(中文):标题生成失败不能影响 session 主流程。
@@ -1 +1 @@
1
- {"version":3,"file":"SessionTitle.js","sourceRoot":"","sources":["../../src/session/SessionTitle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SessionTitle.js","sourceRoot":"","sources":["../../src/session/SessionTitle.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@downcity/agent",
3
- "version": "1.1.205",
3
+ "version": "1.1.207",
4
4
  "type": "module",
5
5
  "description": "Downcity Agent 运行时 — 单 Agent 执行壳与本机 RPC 能力",
6
6
  "main": "./bin/index.js",
@@ -17,8 +17,8 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@ai-sdk/openai-compatible": "^2.0.48",
20
- "@downcity/shell": "^0.1.71",
21
- "@downcity/type": "0.1.104",
20
+ "@downcity/shell": "^0.1.72",
21
+ "@downcity/type": "0.1.105",
22
22
  "ai": "^6.0.193",
23
23
  "commander": "^15.0.0",
24
24
  "dotenv": "^17.4.2",
@@ -0,0 +1,105 @@
1
+ /**
2
+ * @file 验证 Agent 动态 env 会进入 shell safe sandbox。
3
+ *
4
+ * 关键点(中文)
5
+ * - 测试编译后的 package 入口,确保 SDK 用户拿到的行为与源码一致。
6
+ * - 只允许 Agent env 中显式存在的 key 进入 safe sandbox,不继承完整宿主环境。
7
+ */
8
+
9
+ import test from "node:test";
10
+ import assert from "node:assert/strict";
11
+ import fs from "node:fs/promises";
12
+ import os from "node:os";
13
+ import path from "node:path";
14
+
15
+ import { Agent } from "@downcity/agent";
16
+ import { Shell } from "@downcity/shell";
17
+ import {
18
+ checkShellSandboxPreflight,
19
+ } from "@downcity/shell/sandbox/SandboxPreflight.js";
20
+
21
+ async function execute_shell(agent, cmd) {
22
+ const result = await agent.tools.shell_exec.execute(
23
+ {
24
+ cmd,
25
+ shell: "/bin/sh",
26
+ login: false,
27
+ timeout_ms: 5000,
28
+ sandbox: "safe",
29
+ },
30
+ { toolCallId: "agent-env-shell-sandbox-test" },
31
+ );
32
+ assert.equal(result.success, true, result.error || result.output);
33
+ return String(result.output || "");
34
+ }
35
+
36
+ test("agent setEnv and patchEnv are visible in shell safe sandbox", async (t) => {
37
+ const preflight = await checkShellSandboxPreflight();
38
+ if (!preflight.ok) {
39
+ t.skip(`safe sandbox unavailable: ${preflight.issues.map((issue) => issue.message).join("; ")}`);
40
+ return;
41
+ }
42
+
43
+ const root_path = await fs.mkdtemp(path.join(os.tmpdir(), "downcity-agent-env-"));
44
+ const previous_host_value = process.env.HOST_ONLY_ENV_REPRO;
45
+ process.env.HOST_ONLY_ENV_REPRO = "host_secret";
46
+
47
+ const agent = new Agent({
48
+ id: "agent-env-shell-sandbox-test",
49
+ path: root_path,
50
+ shell: new Shell(),
51
+ });
52
+
53
+ try {
54
+ agent.setEnv({
55
+ DYNAMIC_ENV_REPRO: "initial_value",
56
+ DC_DYNAMIC_REPRO: "dc_initial",
57
+ REMOVED_ENV_REPRO: "remove_me",
58
+ });
59
+
60
+ const first_output = await execute_shell(
61
+ agent,
62
+ 'printf "CURRENT=%s\\nDC=%s\\nREMOVED=%s\\nHOST_ONLY=%s\\n" "$DYNAMIC_ENV_REPRO" "$DC_DYNAMIC_REPRO" "$REMOVED_ENV_REPRO" "$HOST_ONLY_ENV_REPRO"',
63
+ );
64
+ assert.match(first_output, /CURRENT=initial_value/);
65
+ assert.match(first_output, /DC=dc_initial/);
66
+ assert.match(first_output, /REMOVED=remove_me/);
67
+ assert.match(first_output, /HOST_ONLY=\n/);
68
+
69
+ agent.getShell()?.configure({
70
+ env: {
71
+ DYNAMIC_ENV_REPRO: "configured_value",
72
+ },
73
+ });
74
+
75
+ const configured_output = await execute_shell(
76
+ agent,
77
+ 'printf "CURRENT=%s\\nHOST_ONLY=%s\\n" "$DYNAMIC_ENV_REPRO" "$HOST_ONLY_ENV_REPRO"',
78
+ );
79
+ assert.match(configured_output, /CURRENT=configured_value/);
80
+ assert.match(configured_output, /HOST_ONLY=\n/);
81
+
82
+ agent.patchEnv({
83
+ DYNAMIC_ENV_REPRO: "updated_value",
84
+ ADDED_ENV_REPRO: "added_value",
85
+ REMOVED_ENV_REPRO: null,
86
+ });
87
+
88
+ const second_output = await execute_shell(
89
+ agent,
90
+ 'printf "CURRENT=%s\\nADDED=%s\\nREMOVED=%s\\nHOST_ONLY=%s\\n" "$DYNAMIC_ENV_REPRO" "$ADDED_ENV_REPRO" "$REMOVED_ENV_REPRO" "$HOST_ONLY_ENV_REPRO"',
91
+ );
92
+ assert.match(second_output, /CURRENT=updated_value/);
93
+ assert.match(second_output, /ADDED=added_value/);
94
+ assert.match(second_output, /REMOVED=\n/);
95
+ assert.match(second_output, /HOST_ONLY=\n/);
96
+ } finally {
97
+ await agent.dispose();
98
+ await fs.rm(root_path, { recursive: true, force: true });
99
+ if (previous_host_value === undefined) {
100
+ delete process.env.HOST_ONLY_ENV_REPRO;
101
+ } else {
102
+ process.env.HOST_ONLY_ENV_REPRO = previous_host_value;
103
+ }
104
+ }
105
+ });
@@ -159,10 +159,9 @@ test("session.prompt waits for agent background ready before model execution", a
159
159
  const result = await turn.finished;
160
160
 
161
161
  assert.equal(result.success, true);
162
- assert.equal(model_stream_calls, 1);
162
+ assert.equal(model_stream_calls, 2);
163
163
  } finally {
164
164
  lifecycle_ready.resolve();
165
165
  await agent.dispose();
166
166
  }
167
167
  });
168
-
@@ -16,39 +16,64 @@ import fs from "node:fs/promises";
16
16
  import { MockLanguageModelV3 } from "ai/test";
17
17
  import { Agent } from "../bin/index.js";
18
18
 
19
+ function create_stream_text_result(text) {
20
+ return {
21
+ stream: new ReadableStream({
22
+ start(controller) {
23
+ controller.enqueue({
24
+ type: "stream-start",
25
+ warnings: [],
26
+ });
27
+ controller.enqueue({
28
+ type: "text-start",
29
+ id: "text_1",
30
+ });
31
+ controller.enqueue({
32
+ type: "text-delta",
33
+ id: "text_1",
34
+ delta: text,
35
+ });
36
+ controller.enqueue({
37
+ type: "text-end",
38
+ id: "text_1",
39
+ });
40
+ controller.enqueue({
41
+ type: "finish",
42
+ finishReason: {
43
+ unified: "stop",
44
+ raw: "stop",
45
+ },
46
+ usage: {
47
+ inputTokens: {
48
+ total: 0,
49
+ noCache: 0,
50
+ cacheRead: 0,
51
+ cacheWrite: 0,
52
+ },
53
+ outputTokens: {
54
+ total: 0,
55
+ text: 0,
56
+ reasoning: 0,
57
+ },
58
+ },
59
+ });
60
+ controller.close();
61
+ },
62
+ }),
63
+ };
64
+ }
65
+
19
66
  function create_mock_title_model(title_text) {
20
67
  return new MockLanguageModelV3({
21
68
  modelId: "mock-session-title-model",
22
- doGenerate: async () => ({
23
- content: [
24
- {
25
- type: "text",
26
- text: title_text,
27
- },
28
- ],
29
- finishReason: "stop",
30
- usage: {
31
- inputTokens: {
32
- total: 0,
33
- noCache: 0,
34
- cacheRead: 0,
35
- cacheWrite: 0,
36
- },
37
- outputTokens: {
38
- total: 0,
39
- text: 0,
40
- reasoning: 0,
41
- },
42
- },
43
- warnings: [],
44
- }),
69
+ doStream: async () => create_stream_text_result(title_text),
45
70
  });
46
71
  }
47
72
 
48
73
  function create_failing_title_model() {
49
74
  return new MockLanguageModelV3({
50
75
  modelId: "mock-session-title-failing-model",
51
- doGenerate: async () => {
76
+ doStream: async () => {
52
77
  throw new Error("mock title generation failed");
53
78
  },
54
79
  });
@@ -12,6 +12,9 @@ import path from "node:path";
12
12
 
13
13
  import { buildMacOsSeatbeltSandboxEnv } from "@downcity/shell/sandbox/MacOsSeatbeltSandbox.js";
14
14
  import { buildLinuxBubblewrapSandboxEnv } from "@downcity/shell/sandbox/LinuxBubblewrapSandbox.js";
15
+ import {
16
+ resolveSandboxEnvAllowlist,
17
+ } from "@downcity/shell/sandbox/SandboxConfigResolver.js";
15
18
 
16
19
  function createParams() {
17
20
  const rootPath = "/tmp/downcity-shell-env/project";
@@ -73,3 +76,18 @@ test("Linux bubblewrap sandbox env points zsh TMPPREFIX at sandbox tmp", () => {
73
76
 
74
77
  assertSandboxTmpEnv(env, params.config.tmpDir);
75
78
  });
79
+
80
+ test("safe sandbox env allowlist includes explicit agent env keys only", () => {
81
+ const allowlist = resolveSandboxEnvAllowlist({
82
+ rootPath: "/tmp/downcity-shell-env/project",
83
+ env: {
84
+ DYNAMIC_ENV_REPRO: "dynamic_value",
85
+ DC_DYNAMIC_REPRO: "dc_value",
86
+ },
87
+ });
88
+
89
+ assert.equal(allowlist.includes("PATH"), true);
90
+ assert.equal(allowlist.includes("DYNAMIC_ENV_REPRO"), true);
91
+ assert.equal(allowlist.includes("DC_DYNAMIC_REPRO"), true);
92
+ assert.equal(allowlist.includes("HOST_ONLY_ENV_REPRO"), false);
93
+ });
@@ -7,7 +7,8 @@
7
7
  * - 当 title 仍为空时,后续执行链路可以再次尝试生成。
8
8
  */
9
9
 
10
- import { generateText, type LanguageModel } from "ai";
10
+ import { streamText, type LanguageModel } from "ai";
11
+ import { buildOpenAIResponsesProviderOptions } from "@executor/messages/SessionMessageCodec.js";
11
12
  import type { SessionHistoryMetaV1 } from "@/executor/types/SessionHistoryMeta.js";
12
13
  import type { SessionRecordV1 } from "@/executor/types/SessionRecords.js";
13
14
  import { is_session_message_record } from "@/executor/types/SessionRecords.js";
@@ -203,8 +204,9 @@ async function generateSessionTitle(input: {
203
204
  */
204
205
  logger?: Logger;
205
206
  }): Promise<string | undefined> {
207
+ let observedStreamError: unknown;
206
208
  try {
207
- const result = await generateText({
209
+ const result = streamText({
208
210
  model: input.model,
209
211
  system:
210
212
  "You generate minimal conversation titles. Output only the title itself, with no explanation and no quotation marks.",
@@ -214,8 +216,13 @@ async function generateSessionTitle(input: {
214
216
  "",
215
217
  input.firstUserText,
216
218
  ].join("\n"),
219
+ providerOptions: buildOpenAIResponsesProviderOptions(),
220
+ onError: ({ error }) => {
221
+ observedStreamError = error;
222
+ },
217
223
  });
218
- const generatedTitle = normalizeGeneratedTitle(result.text);
224
+ const text = await result.text;
225
+ const generatedTitle = normalizeGeneratedTitle(text);
219
226
  if (!generatedTitle) {
220
227
  await logSessionTitleDiagnostic({
221
228
  logger: input.logger,
@@ -225,12 +232,13 @@ async function generateSessionTitle(input: {
225
232
  details: {
226
233
  modelLabel: input.modelLabel || null,
227
234
  firstUserTextLength: input.firstUserText.length,
228
- rawTitleLength: String(result.text || "").length,
235
+ rawTitleLength: String(text || "").length,
229
236
  },
230
237
  });
231
238
  }
232
239
  return generatedTitle;
233
240
  } catch (error) {
241
+ const effectiveError = observedStreamError || error;
234
242
  await logSessionTitleDiagnostic({
235
243
  logger: input.logger,
236
244
  sessionId: input.sessionId,
@@ -239,7 +247,7 @@ async function generateSessionTitle(input: {
239
247
  details: {
240
248
  modelLabel: input.modelLabel || null,
241
249
  firstUserTextLength: input.firstUserText.length,
242
- ...summarizeTitleError(error),
250
+ ...summarizeTitleError(effectiveError),
243
251
  },
244
252
  });
245
253
  // 关键点(中文):标题生成失败不能影响 session 主流程。