@downcity/plugins 1.0.30 → 1.0.35

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 (87) hide show
  1. package/bin/BuiltinPlugins.js +2 -2
  2. package/bin/BuiltinPlugins.js.map +1 -1
  3. package/bin/asr/Config.d.ts +1 -1
  4. package/bin/asr/Config.d.ts.map +1 -1
  5. package/bin/auth/Plugin.d.ts +6 -6
  6. package/bin/auth/Plugin.d.ts.map +1 -1
  7. package/bin/auth/Plugin.js +29 -20
  8. package/bin/auth/Plugin.js.map +1 -1
  9. package/bin/auth/types/AuthPlugin.d.ts +19 -19
  10. package/bin/auth/types/AuthPlugin.d.ts.map +1 -1
  11. package/bin/auth/types/AuthPlugin.js +17 -17
  12. package/bin/auth/types/AuthPlugin.js.map +1 -1
  13. package/bin/chat/Action.js +4 -4
  14. package/bin/chat/Action.js.map +1 -1
  15. package/bin/chat/ChatPlugin.d.ts +1 -1
  16. package/bin/chat/ChatPlugin.d.ts.map +1 -1
  17. package/bin/chat/accounts/Store.d.ts +1 -1
  18. package/bin/chat/accounts/Store.d.ts.map +1 -1
  19. package/bin/chat/runtime/ChatChannelConfig.d.ts +1 -1
  20. package/bin/chat/runtime/ChatChannelConfig.d.ts.map +1 -1
  21. package/bin/chat/runtime/ChatChannelCore.d.ts +1 -1
  22. package/bin/chat/runtime/ChatChannelCore.d.ts.map +1 -1
  23. package/bin/chat/runtime/ChatQueueWorker.d.ts.map +1 -1
  24. package/bin/chat/runtime/ChatQueueWorker.js +2 -3
  25. package/bin/chat/runtime/ChatQueueWorker.js.map +1 -1
  26. package/bin/chat/runtime/ChatSession.d.ts.map +1 -1
  27. package/bin/chat/runtime/ChatSession.js +11 -2
  28. package/bin/chat/runtime/ChatSession.js.map +1 -1
  29. package/bin/chat/runtime/PluginDispatch.js.map +1 -1
  30. package/bin/chat/runtime/PluginPoints.d.ts +3 -3
  31. package/bin/chat/runtime/PluginPoints.js +3 -3
  32. package/bin/chat/runtime/SystemPrompt.js +2 -2
  33. package/bin/chat/runtime/SystemPrompt.js.map +1 -1
  34. package/bin/chat/types/ChannelAccount.d.ts +1 -1
  35. package/bin/chat/types/ChannelAccount.d.ts.map +1 -1
  36. package/bin/index.d.ts +3 -3
  37. package/bin/index.d.ts.map +1 -1
  38. package/bin/index.js +2 -2
  39. package/bin/index.js.map +1 -1
  40. package/bin/shell/runtime/ShellActionRuntime.js +1 -1
  41. package/bin/shell/runtime/ShellActionRuntime.js.map +1 -1
  42. package/bin/shell/runtime/ShellRuntimeEnvironment.d.ts.map +1 -1
  43. package/bin/shell/runtime/ShellRuntimeEnvironment.js +9 -6
  44. package/bin/shell/runtime/ShellRuntimeEnvironment.js.map +1 -1
  45. package/bin/skill/runtime/SystemProvider.js +3 -3
  46. package/bin/skill/runtime/SystemProvider.js.map +1 -1
  47. package/bin/task/runtime/TaskRunnerRound.d.ts.map +1 -1
  48. package/bin/task/runtime/TaskRunnerRound.js +19 -5
  49. package/bin/task/runtime/TaskRunnerRound.js.map +1 -1
  50. package/bin/voice/Config.d.ts +1 -1
  51. package/bin/voice/Config.d.ts.map +1 -1
  52. package/bin/workboard/runtime/Collector.d.ts.map +1 -1
  53. package/bin/workboard/runtime/Collector.js +3 -5
  54. package/bin/workboard/runtime/Collector.js.map +1 -1
  55. package/bin/workboard/runtime/Normalizer.d.ts +3 -3
  56. package/bin/workboard/runtime/Normalizer.d.ts.map +1 -1
  57. package/bin/workboard/runtime/Normalizer.js.map +1 -1
  58. package/bin/workboard/runtime/SessionSummary.d.ts +47 -0
  59. package/bin/workboard/runtime/SessionSummary.d.ts.map +1 -0
  60. package/bin/workboard/runtime/SessionSummary.js +51 -0
  61. package/bin/workboard/runtime/SessionSummary.js.map +1 -0
  62. package/package.json +2 -2
  63. package/src/BuiltinPlugins.ts +2 -2
  64. package/src/asr/Config.ts +1 -1
  65. package/src/auth/Plugin.ts +151 -134
  66. package/src/auth/types/AuthPlugin.ts +24 -24
  67. package/src/chat/Action.ts +4 -4
  68. package/src/chat/ChatPlugin.ts +1 -1
  69. package/src/chat/accounts/ChannelAccountManager.ts +2 -2
  70. package/src/chat/accounts/Store.ts +1 -1
  71. package/src/chat/runtime/ChatChannelConfig.ts +1 -1
  72. package/src/chat/runtime/ChatChannelCore.ts +1 -1
  73. package/src/chat/runtime/ChatQueueWorker.ts +1 -5
  74. package/src/chat/runtime/ChatSession.ts +12 -2
  75. package/src/chat/runtime/PluginDispatch.ts +2 -2
  76. package/src/chat/runtime/PluginPoints.ts +3 -3
  77. package/src/chat/runtime/SystemPrompt.ts +2 -2
  78. package/src/chat/types/ChannelAccount.ts +1 -1
  79. package/src/index.ts +6 -6
  80. package/src/shell/runtime/ShellActionRuntime.ts +1 -1
  81. package/src/shell/runtime/ShellRuntimeEnvironment.ts +10 -6
  82. package/src/skill/runtime/SystemProvider.ts +3 -3
  83. package/src/task/runtime/TaskRunnerRound.ts +22 -5
  84. package/src/voice/Config.ts +1 -1
  85. package/src/workboard/runtime/Collector.ts +3 -5
  86. package/src/workboard/runtime/Normalizer.ts +5 -5
  87. package/src/workboard/runtime/SessionSummary.ts +92 -0
@@ -56,7 +56,7 @@ export const CHAT_PLUGIN_POINTS = {
56
56
  *
57
57
  * 说明(中文)
58
58
  * - 仅做副作用记录,不返回值。
59
- * - 典型实现方是 auth plugin。
59
+ * - 典型实现方是 chat-authorization plugin。
60
60
  */
61
61
  observePrincipal: "chat.observePrincipal",
62
62
  /**
@@ -64,7 +64,7 @@ export const CHAT_PLUGIN_POINTS = {
64
64
  *
65
65
  * 说明(中文)
66
66
  * - 由 chat plugin runtime 在 ingress 阶段显式调用。
67
- * - 典型实现方是 auth plugin。
67
+ * - 典型实现方是 chat-authorization plugin。
68
68
  */
69
69
  authorizeIncoming: "chat.authorizeIncoming",
70
70
  /**
@@ -72,7 +72,7 @@ export const CHAT_PLUGIN_POINTS = {
72
72
  *
73
73
  * 说明(中文)
74
74
  * - 主要用于在 history / queue metadata 中补齐授权上下文。
75
- * - 典型实现方是 auth plugin。
75
+ * - 典型实现方是 chat-authorization plugin。
76
76
  */
77
77
  resolveUserRole: "chat.resolveUserRole",
78
78
  } as const;
@@ -7,7 +7,7 @@
7
7
  * - 统一从 request context + ChatMetaStore 读取当前 chat 元信息。
8
8
  */
9
9
 
10
- import { getSessionRunScope } from "@downcity/agent/internal/executor/SessionRunScope.js";
10
+ import { getSessionRunContext } from "@downcity/agent/internal/executor/SessionRunScope.js";
11
11
  import type { AgentContext } from "@downcity/agent/internal/types/runtime/agent/AgentContext.js";
12
12
  import type { ChatEnvironmentPromptInput } from "@/chat/types/ChatPromptContext.js";
13
13
  import { readChatMetaBySessionId } from "@/chat/runtime/ChatMetaStore.js";
@@ -26,7 +26,7 @@ function normalizePromptValue(value: unknown, fallback: string): string {
26
26
  export async function resolveCurrentChatEnvironmentPromptInput(
27
27
  context: AgentContext,
28
28
  ): Promise<ChatEnvironmentPromptInput | null> {
29
- const sessionId = String(getSessionRunScope()?.sessionId || "").trim();
29
+ const sessionId = String(getSessionRunContext()?.sessionId || "").trim();
30
30
  if (!sessionId) return null;
31
31
 
32
32
  const meta = await readChatMetaBySessionId({
@@ -6,7 +6,7 @@
6
6
  * - 凭据明文只允许出现在写入输入中,读取结果必须是脱敏后的安全视图。
7
7
  */
8
8
 
9
- import type { StoredChannelAccountChannel } from "@downcity/agent/internal/types/runtime/host/Store.js";
9
+ import type { StoredChannelAccountChannel } from "@downcity/agent/internal/types/platform/Store.js";
10
10
 
11
11
  /**
12
12
  * 支持的 chat channel account 类型。
package/src/index.ts CHANGED
@@ -14,7 +14,7 @@ export {
14
14
  export type { BuiltinPluginClass } from "./BuiltinPlugins.js";
15
15
  export { ChatPlugin } from "./chat/ChatPlugin.js";
16
16
  export { ChatChannelAccountManager } from "./chat/accounts/ChannelAccountManager.js";
17
- export { AuthPlugin } from "./auth/Plugin.js";
17
+ export { ChatAuthorizationPlugin } from "./auth/Plugin.js";
18
18
  export { SkillPlugin } from "./skill/Plugin.js";
19
19
  export { WebPlugin } from "./web/Plugin.js";
20
20
  export { AsrPlugin } from "./asr/Plugin.js";
@@ -28,6 +28,7 @@ export {
28
28
  export { resolveAuthorizedUserRole } from "./auth/runtime/AuthorizationPolicy.js";
29
29
  export {
30
30
  CHAT_AUTHORIZATION_CHANNELS,
31
+ CHAT_AUTHORIZATION_PLUGIN_NAME,
31
32
  createDefaultChatAuthorizationRoles,
32
33
  isChatAuthorizationChannel,
33
34
  } from "./auth/types/AuthPlugin.js";
@@ -42,23 +43,22 @@ export type {
42
43
  ChatPluginTelegramOptions,
43
44
  } from "./chat/ChatPluginTypes.js";
44
45
  export type {
45
- AuthObservePrincipalPayload,
46
- AuthObservePrincipalResult,
47
- AuthResolveUserRolePayload,
48
- AuthSetUserRolePayload,
49
- AuthWriteConfigPayload,
50
46
  ChatAuthorizationCatalog,
51
47
  ChatAuthorizationChannel,
52
48
  ChatAuthorizationConfig,
53
49
  ChatAuthorizationDecision,
54
50
  ChatAuthorizationEvaluateInput,
55
51
  ChatAuthorizationEvaluateResult,
52
+ ChatAuthorizationObservePrincipalPayload,
56
53
  ChatAuthorizationObservedChat,
57
54
  ChatAuthorizationObservedUser,
58
55
  ChatAuthorizationPermission,
59
56
  ChatAuthorizationPermissionMeta,
57
+ ChatAuthorizationResolveUserRolePayload,
60
58
  ChatAuthorizationRole,
59
+ ChatAuthorizationSetUserRolePayload,
61
60
  ChatAuthorizationSnapshot,
62
61
  ChatAuthorizationStateFile,
62
+ ChatAuthorizationWriteConfigPayload,
63
63
  ChatChannelAuthorizationConfig,
64
64
  } from "./auth/types/AuthPlugin.js";
@@ -9,7 +9,7 @@
9
9
 
10
10
  import fs from "fs-extra";
11
11
  import type { AgentContext } from "@downcity/agent/internal/types/runtime/agent/AgentContext.js";
12
- import { spawnShellProcess } from "@downcity/agent/internal/runtime/sandbox/SandboxRunner.js";
12
+ import { spawnShellProcess } from "@downcity/agent/internal/sandbox/SandboxRunner.js";
13
13
  import type {
14
14
  ShellPluginState,
15
15
  ShellSessionRuntimeState,
@@ -8,8 +8,12 @@
8
8
 
9
9
  import path from "node:path";
10
10
  import type { AgentContext } from "@downcity/agent/internal/types/runtime/agent/AgentContext.js";
11
- import { stripInvocationAuthEnv } from "@downcity/agent/internal/runtime/auth/AuthEnv.js";
12
- import { getSessionRunScope } from "@downcity/agent/internal/executor/SessionRunScope.js";
11
+ import { getSessionRunContext } from "@downcity/agent/internal/executor/SessionRunScope.js";
12
+
13
+ function stripShellSecretEnv(env: NodeJS.ProcessEnv): void {
14
+ delete env.DC_AUTH_TOKEN;
15
+ delete env.DC_AGENT_TOKEN;
16
+ }
13
17
 
14
18
  /**
15
19
  * 构造 shell 子进程环境变量。
@@ -27,8 +31,8 @@ export function buildShellEnv(context: AgentContext): NodeJS.ProcessEnv {
27
31
  env[normalizedKey] = normalizedValue;
28
32
  }
29
33
 
30
- const request = getSessionRunScope();
31
- const sessionId = String(request?.sessionId || "").trim();
34
+ const run_context = getSessionRunContext();
35
+ const sessionId = String(run_context?.sessionId || "").trim();
32
36
  const agentPath = String(context.rootPath || "").trim();
33
37
  const configuredAgentId = String(context.config?.id || "").trim();
34
38
  const agentId = configuredAgentId || (agentPath ? path.basename(agentPath) : "");
@@ -42,7 +46,7 @@ export function buildShellEnv(context: AgentContext): NodeJS.ProcessEnv {
42
46
  if (sessionId) env.DC_SESSION_ID = sessionId;
43
47
  if (process.env.DC_CITY_HOST) env.DC_CITY_HOST = process.env.DC_CITY_HOST;
44
48
  if (process.env.DC_CITY_PORT) env.DC_CITY_PORT = process.env.DC_CITY_PORT;
45
- stripInvocationAuthEnv(env);
49
+ stripShellSecretEnv(env);
46
50
 
47
51
  return env;
48
52
  }
@@ -62,6 +66,6 @@ export function resolveShellCwd(context: AgentContext, cwd?: string): string {
62
66
  export function resolveOwnerContextId(explicit?: string): string | undefined {
63
67
  const fromInput = String(explicit || "").trim();
64
68
  if (fromInput) return fromInput;
65
- const fromRequest = String(getSessionRunScope()?.sessionId || "").trim();
69
+ const fromRequest = String(getSessionRunContext()?.sessionId || "").trim();
66
70
  return fromRequest || undefined;
67
71
  }
@@ -7,7 +7,7 @@
7
7
  * - `lookup` 行为为无状态:由 action 读取 SKILL.md 后通过协议注入 user message
8
8
  */
9
9
 
10
- import { getSessionRunScope } from "@downcity/agent/internal/executor/SessionRunScope.js";
10
+ import { getSessionRunContext } from "@downcity/agent/internal/executor/SessionRunScope.js";
11
11
  import type { DowncityConfig } from "@downcity/agent/internal/types/config/DowncityConfig.js";
12
12
  import { discoverClaudeSkillsSync } from "./Discovery.js";
13
13
  import { renderClaudeSkillsPromptSection } from "./Prompt.js";
@@ -19,8 +19,8 @@ type SkillSystemRuntime = {
19
19
  };
20
20
 
21
21
  function getCurrentSessionId(): string {
22
- const request = getSessionRunScope();
23
- return String(request?.sessionId || "").trim();
22
+ const run_context = getSessionRunContext();
23
+ return String(run_context?.sessionId || "").trim();
24
24
  }
25
25
 
26
26
  /**
@@ -8,10 +8,10 @@
8
8
 
9
9
  import path from "node:path";
10
10
  import fs from "fs-extra";
11
- import { stripInvocationAuthEnv } from "@downcity/agent/internal/runtime/auth/AuthEnv.js";
12
11
  import type { AgentContext } from "@downcity/agent/internal/types/runtime/agent/AgentContext.js";
13
- import { runSandboxCommand } from "@downcity/agent/internal/runtime/sandbox/SandboxRunner.js";
12
+ import { runSandboxCommand } from "@downcity/agent/internal/sandbox/SandboxRunner.js";
14
13
  import type { SessionRunResult } from "@downcity/agent/internal/executor/types/SessionRun.js";
14
+ import type { SessionRunContext } from "@downcity/agent/internal/types/executor/SessionRunContext.js";
15
15
  import type { JsonObject } from "@downcity/agent/internal/types/common/Json.js";
16
16
  import type {
17
17
  ChatSendOutputPick,
@@ -23,6 +23,11 @@ import type {
23
23
  import { withSessionRunScope } from "@downcity/agent/internal/executor/SessionRunScope.js";
24
24
  import { appendTaskRoundUserMessage } from "./TaskRunnerSession.js";
25
25
 
26
+ function stripTaskSecretEnv(env: NodeJS.ProcessEnv): void {
27
+ delete env.DC_AUTH_TOKEN;
28
+ delete env.DC_AGENT_TOKEN;
29
+ }
30
+
26
31
  /**
27
32
  * 从文本中提取 JSON 对象(支持 ```json 代码块)。
28
33
  */
@@ -241,7 +246,7 @@ export async function runAgentRound(params: {
241
246
 
242
247
  const result = await withSessionRunScope(
243
248
  {
244
- sessionId: params.sessionId,
249
+ runContext: create_task_run_context(params.sessionId),
245
250
  },
246
251
  () =>
247
252
  params.taskSessionRuntime.getExecutor(params.sessionId).run({
@@ -282,13 +287,13 @@ export async function runScriptTask(params: {
282
287
  await fs.writeFile(scriptAbs, body.endsWith("\n") ? body : `${body}\n`, "utf-8");
283
288
 
284
289
  const execResult = await withSessionRunScope(
285
- { sessionId: params.sessionId },
290
+ { runContext: create_task_run_context(params.sessionId) },
286
291
  () => {
287
292
  const childEnv: NodeJS.ProcessEnv = {
288
293
  ...process.env,
289
294
  DC_SESSION_ID: params.sessionId,
290
295
  };
291
- stripInvocationAuthEnv(childEnv);
296
+ stripTaskSecretEnv(childEnv);
292
297
  return runSandboxCommand({
293
298
  context: params.context,
294
299
  shellId: `task-script:${params.sessionId}`,
@@ -310,6 +315,18 @@ export async function runScriptTask(params: {
310
315
  };
311
316
  }
312
317
 
318
+ /**
319
+ * 创建 task runner 的显式 session run context。
320
+ */
321
+ function create_task_run_context(session_id: string): SessionRunContext {
322
+ return {
323
+ sessionId: session_id,
324
+ injectedUserMessages: [],
325
+ deferredPersistedUserMessages: [],
326
+ pendingAssistantFileParts: [],
327
+ };
328
+ }
329
+
313
330
  /**
314
331
  * 校验任务结果是否满足“必须有结果”的规则。
315
332
  */
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { VoicePluginConfig } from "@/voice/types/VoicePlugin.js";
10
- import type { AgentPluginConfigRuntime } from "@downcity/agent/internal/types/runtime/host/AgentHost.js";
10
+ import type { AgentPluginConfigRuntime } from "@downcity/agent/internal/types/agent/AgentRuntimeAssembly.js";
11
11
  import type { JsonObject, JsonValue } from "@downcity/agent/internal/types/common/Json.js";
12
12
 
13
13
  /**
@@ -8,9 +8,9 @@
8
8
 
9
9
  import { listTaskDefinitions } from "@/task/Action.js";
10
10
  import { listPluginStates } from "@downcity/agent/internal/plugin/core/Manager.js";
11
- import { listControlSessionSummaries } from "@downcity/agent/internal/runtime/control/SessionSummaryStore.js";
12
11
  import type { AgentContext } from "@downcity/agent/internal/types/runtime/agent/AgentContext.js";
13
12
  import type { WorkboardSnapshot } from "@/workboard/types/Workboard.js";
13
+ import { listWorkboardSessionSummaries } from "@/workboard/runtime/SessionSummary.js";
14
14
  import {
15
15
  buildIdleActivity,
16
16
  toRecentActivity,
@@ -30,10 +30,8 @@ export async function collectWorkboardSnapshot(
30
30
  ): Promise<WorkboardSnapshot> {
31
31
  const collectedAt = new Date().toISOString();
32
32
  const executingSessionIds = new Set(context.session.listExecutingSessionIds());
33
- const sessions = await listControlSessionSummaries({
34
- projectRoot: context.rootPath,
35
- agentId: context.paths.agentId,
36
- executionContext: context,
33
+ const sessions = await listWorkboardSessionSummaries({
34
+ context,
37
35
  limit: WORKBOARD_RECENT_LIMIT + Math.max(executingSessionIds.size, 1),
38
36
  executingSessionIds,
39
37
  });
@@ -6,9 +6,9 @@
6
6
  * - 所有会泄漏上下文的字段都必须在这里截断或抽象。
7
7
  */
8
8
 
9
- import type { ControlSessionSummary } from "@downcity/agent/internal/runtime/control/types/ControlViewData.js";
10
9
  import type { PluginStateSnapshot } from "@downcity/agent/internal/plugin/types/PluginState.js";
11
10
  import type { TaskListResponse } from "@/task/types/TaskCommand.js";
11
+ import type { WorkboardSessionSummary } from "@/workboard/runtime/SessionSummary.js";
12
12
  import type {
13
13
  WorkboardActivityItem,
14
14
  WorkboardAgentSummary,
@@ -59,7 +59,7 @@ function buildMomentum(params: { currentCount: number; recentCount: number }): s
59
59
  return "安静";
60
60
  }
61
61
 
62
- function buildRunningSummary(item: ControlSessionSummary): string {
62
+ function buildRunningSummary(item: WorkboardSessionSummary): string {
63
63
  const messageCount = typeof item.messageCount === "number" ? item.messageCount : 0;
64
64
  if (messageCount >= 24) return "正在延展一段较长的工作脉络,并持续生成新的内容。";
65
65
  if (messageCount >= 8) return "正在承接连续输入,逐步形成新的阶段结果。";
@@ -67,7 +67,7 @@ function buildRunningSummary(item: ControlSessionSummary): string {
67
67
  return "当前处于活跃展开之中。";
68
68
  }
69
69
 
70
- function buildRecentSummary(item: ControlSessionSummary, index: number): string {
70
+ function buildRecentSummary(item: WorkboardSessionSummary, index: number): string {
71
71
  const messageCount = typeof item.messageCount === "number" ? item.messageCount : 0;
72
72
  if (index === 0 && messageCount > 0) return "刚刚完成一段新的展开,正在短暂停留。";
73
73
  if (messageCount >= 12) return "近期完成了一次较长的内容延展。";
@@ -103,7 +103,7 @@ export function toWorkboardAgentSummary(params: {
103
103
  * 将运行中的 session 映射为对外安全活动项。
104
104
  */
105
105
  export function toRunningActivity(params: {
106
- item: ControlSessionSummary;
106
+ item: WorkboardSessionSummary;
107
107
  index: number;
108
108
  }): WorkboardActivityItem {
109
109
  const title = params.index === 0 ? "当前主线" : `当前并行线 ${params.index + 1}`;
@@ -123,7 +123,7 @@ export function toRunningActivity(params: {
123
123
  * 将近期 session 映射为对外安全活动项。
124
124
  */
125
125
  export function toRecentActivity(params: {
126
- item: ControlSessionSummary;
126
+ item: WorkboardSessionSummary;
127
127
  index: number;
128
128
  }): WorkboardActivityItem {
129
129
  const title = params.index === 0 ? "最近一次更新" : `近期片段 ${params.index + 1}`;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Workboard session 摘要采集。
3
+ *
4
+ * 关键点(中文)
5
+ * - workboard 只需要模糊运行态,不复用 Town control 视图模型。
6
+ * - 这里只读取消息数量、更新时间与执行中状态,不暴露消息内容。
7
+ */
8
+
9
+ import fs from "fs-extra";
10
+ import path from "node:path";
11
+ import type { AgentContext } from "@downcity/agent/internal/types/runtime/agent/AgentContext.js";
12
+
13
+ /**
14
+ * Workboard 内部 session 摘要。
15
+ */
16
+ export interface WorkboardSessionSummary {
17
+ /**
18
+ * session 稳定标识,仅供内部排序和执行态匹配使用,不会进入公开输出。
19
+ */
20
+ sessionId: string;
21
+ /**
22
+ * 当前 session 消息数量。
23
+ */
24
+ messageCount: number;
25
+ /**
26
+ * 最近更新时间戳。
27
+ */
28
+ updatedAt?: number;
29
+ /**
30
+ * 当前 session 是否仍在执行。
31
+ */
32
+ executing?: boolean;
33
+ }
34
+
35
+ /**
36
+ * 采集 workboard 需要的 session 摘要。
37
+ */
38
+ export async function listWorkboardSessionSummaries(params: {
39
+ /**
40
+ * Agent runtime context。
41
+ */
42
+ context: AgentContext;
43
+ /**
44
+ * 返回上限。
45
+ */
46
+ limit: number;
47
+ /**
48
+ * 正在执行的 session id 集合。
49
+ */
50
+ executingSessionIds?: Set<string>;
51
+ }): Promise<WorkboardSessionSummary[]> {
52
+ const root_dir = params.context.paths.getDowncitySessionRootDirPath();
53
+ if (!(await fs.pathExists(root_dir))) return [];
54
+
55
+ const entries = await fs.readdir(root_dir, { withFileTypes: true });
56
+ const items: WorkboardSessionSummary[] = [];
57
+
58
+ for (const entry of entries) {
59
+ if (!entry.isDirectory()) continue;
60
+ const session_id = decodeMaybe(entry.name);
61
+ if (!session_id) continue;
62
+ const messages_path = path.join(
63
+ params.context.paths.getDowncitySessionDirPath(session_id),
64
+ "messages",
65
+ "messages.jsonl",
66
+ );
67
+ const stat = await fs.stat(messages_path).catch(() => null);
68
+ items.push({
69
+ sessionId: session_id,
70
+ messageCount: await countJsonlLines(messages_path),
71
+ ...(stat ? { updatedAt: stat.mtimeMs } : {}),
72
+ ...(params.executingSessionIds?.has(session_id) ? { executing: true } : {}),
73
+ });
74
+ }
75
+
76
+ items.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
77
+ return items.slice(0, Math.max(1, params.limit));
78
+ }
79
+
80
+ async function countJsonlLines(file_path: string): Promise<number> {
81
+ const raw = await fs.readFile(file_path, "utf-8").catch(() => "");
82
+ if (!raw) return 0;
83
+ return raw.split("\n").filter((line) => line.trim()).length;
84
+ }
85
+
86
+ function decodeMaybe(value: string): string {
87
+ try {
88
+ return decodeURIComponent(String(value || "")).trim();
89
+ } catch {
90
+ return String(value || "").trim();
91
+ }
92
+ }