nolo-cli 0.1.41 → 0.1.42

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 (136) hide show
  1. package/README.md +27 -12
  2. package/agent-runtime/agentThread.ts +222 -0
  3. package/agent-runtime/agentThreadAdmission.ts +74 -0
  4. package/agent-runtime/dialogWritePlan.ts +42 -0
  5. package/agent-runtime/hostAdapter.ts +1 -0
  6. package/agent-runtime/index.ts +43 -0
  7. package/agent-runtime/localLoop.ts +31 -2
  8. package/agent-runtime/localToolPolicy.ts +1 -0
  9. package/agent-runtime/localWorkspaceTools.ts +1258 -78
  10. package/agent-runtime/noloWorkspaceTools.ts +20 -1
  11. package/agent-runtime/runtimeToolPolicy.ts +1 -5
  12. package/agent-runtime/runtimeToolSurface.ts +1 -0
  13. package/agentAliases.ts +29 -9
  14. package/agentInternalCommandEntries.ts +11 -3
  15. package/agentMachineCommands.ts +1 -0
  16. package/agentRecordCommands.ts +2 -2
  17. package/agentRecordHelpers.ts +59 -6
  18. package/agentRunCommand.ts +84 -19
  19. package/ai/agent/buildSystemPrompt.ts +14 -0
  20. package/ai/agent/cliExecutor.ts +202 -43
  21. package/ai/agent/createAgentSchema.ts +2 -2
  22. package/ai/agent/liveAudioModel.ts +1 -1
  23. package/ai/agent/streamAgentChatTurn.ts +154 -20
  24. package/ai/agent/streamAgentChatTurnUtils.ts +63 -0
  25. package/ai/ai.locale.ts +70 -0
  26. package/ai/chat/inlineImageUrlsForCustomProvider.ts +7 -2
  27. package/ai/chat/sendOpenAICompletionsRequest.ts +25 -1
  28. package/ai/llm/kimi.ts +0 -1
  29. package/ai/llm/openrouterModels.ts +27 -2
  30. package/ai/memory/policy.ts +49 -0
  31. package/ai/memory/queryShared.ts +10 -4
  32. package/ai/memory/recentRelationshipRecap.ts +4 -0
  33. package/ai/memory/remember.ts +48 -104
  34. package/ai/memory/scope.ts +74 -0
  35. package/ai/memory/types.ts +6 -0
  36. package/ai/memory/understanding.ts +10 -18
  37. package/ai/policy/runtimePolicy.ts +95 -23
  38. package/ai/tools/agent/agentTools.ts +0 -21
  39. package/ai/tools/agent/startAgentDialogTool.ts +2 -1
  40. package/ai/tools/index.ts +2 -20
  41. package/ai/tools/prepareTools.ts +171 -1
  42. package/ai/tools/readXhsProfileTool.ts +80 -69
  43. package/ai/tools/table/createTableTool.ts +11 -0
  44. package/ai/tools/toolPacks.ts +19 -0
  45. package/ai/types.ts +3 -0
  46. package/app/favorite/deletedFavoriteProjection.ts +31 -0
  47. package/app/hooks/deleteDbKey.ts +24 -3
  48. package/app/i18n/translations/interface.locale.ts +48 -16
  49. package/app/types/appSummary.ts +4 -0
  50. package/app/utils/desktopAgentRuntimeTurnClient.ts +2 -0
  51. package/auth/adminPermissions.ts +64 -4
  52. package/auth/routes.ts +5 -0
  53. package/chat/chat.locale.ts +4 -0
  54. package/chat/dialog/actions/handleSendMessageAction.ts +7 -2
  55. package/chat/dialog/dialogAttachmentCleanup.ts +135 -0
  56. package/chat/dialog/dialogSlice.ts +117 -4
  57. package/chat/messages/activityCompletion.ts +97 -0
  58. package/chat/messages/imagePayloadPersistence.ts +22 -0
  59. package/chat/messages/messageContent.ts +20 -16
  60. package/chat/messages/messageSlice.ts +20 -1
  61. package/chat/messages/toolThunks.ts +32 -11
  62. package/chat/messages/types.ts +8 -1
  63. package/chat/messages/web/toolDisplayName.ts +686 -0
  64. package/cli/agentAliases.ts +29 -9
  65. package/cli/agentInternalCommandEntries.ts +11 -3
  66. package/cli/agentMachineCommands.ts +1 -0
  67. package/cli/agentRecordCommands.ts +2 -2
  68. package/cli/agentRecordHelpers.ts +59 -6
  69. package/cli/agentRunCommand.ts +84 -19
  70. package/cli/client/agentRun.ts +65 -253
  71. package/cli/client/localRuntimeAdapter.ts +179 -53
  72. package/cli/commandRegistry.ts +7 -4
  73. package/cli/dialogAttachmentCleanup.ts +151 -0
  74. package/cli/dialogCommands.ts +163 -61
  75. package/cli/dialogInternalCommandEntries.ts +1 -1
  76. package/cli/docCreateCommands.ts +345 -0
  77. package/cli/docInternalCommandEntries.ts +11 -0
  78. package/cli/docPageHelpers.ts +80 -0
  79. package/cli/docSkillHelpers.ts +67 -0
  80. package/cli/docSpaceHelpers.ts +46 -0
  81. package/cli/internalCommandEntries.ts +4 -0
  82. package/cli/machineCommands.ts +18 -34
  83. package/cli/memoryCommands.ts +253 -0
  84. package/cli/memoryInternalCommandEntries.ts +9 -0
  85. package/cli/qoderUsageProbe.ts +252 -0
  86. package/cli/scriptCommandEntries.ts +0 -3
  87. package/cli/secretScan.ts +31 -0
  88. package/cli/tableCommands.ts +248 -13
  89. package/cli/workflowInternalCommandEntries.ts +2 -3
  90. package/client/agentRun.test.ts +68 -84
  91. package/client/agentRun.ts +65 -253
  92. package/client/localRuntimeAdapter.test.ts +505 -74
  93. package/client/localRuntimeAdapter.ts +179 -53
  94. package/commandRegistry.ts +7 -4
  95. package/connector-experimental/capabilities.ts +10 -9
  96. package/create/space/sidebarVisibleTypes.ts +19 -0
  97. package/dialogAttachmentCleanup.ts +151 -0
  98. package/dialogCommands.ts +163 -61
  99. package/dialogInternalCommandEntries.ts +1 -1
  100. package/docCreateCommands.ts +345 -0
  101. package/docInternalCommandEntries.ts +11 -0
  102. package/docPageHelpers.ts +80 -0
  103. package/docSkillHelpers.ts +67 -0
  104. package/docSpaceHelpers.ts +46 -0
  105. package/integrations/google/models.ts +4 -4
  106. package/integrations/openai/generateOpenAIRequestBody.ts +0 -3
  107. package/integrations/xhs-reader/types.ts +36 -3
  108. package/integrations/xhs-reader/url.ts +24 -1
  109. package/internalCommandEntries.ts +4 -0
  110. package/machineCommands.ts +18 -34
  111. package/memoryCommands.ts +253 -0
  112. package/memoryInternalCommandEntries.ts +9 -0
  113. package/package.json +133 -114
  114. package/qoderUsageProbe.ts +252 -0
  115. package/render/contentIcon/types.ts +167 -0
  116. package/render/page/docSlice.ts +30 -3
  117. package/render/page/types.ts +2 -0
  118. package/render/table/createTableAction.ts +4 -0
  119. package/render/table/tableSlice.ts +61 -0
  120. package/render/table/types.ts +10 -0
  121. package/scriptCommandEntries.ts +0 -3
  122. package/secretScan.ts +31 -0
  123. package/server/handlers/agentRun/types.ts +24 -23
  124. package/tableCommands.ts +248 -13
  125. package/workflowInternalCommandEntries.ts +2 -3
  126. package/ai/tools/agent/taskRunTool.ts +0 -112
  127. package/cli/client/taskRunPrompt.ts +0 -27
  128. package/cli/taskRunCommand.ts +0 -265
  129. package/client/taskRunPrompt.ts +0 -27
  130. package/integrations/xhs-reader/analyze/profileAnalyzer.ts +0 -205
  131. package/integrations/xhs-reader/backends/playwrightProfileCollector.ts +0 -624
  132. package/integrations/xhs-reader/bridge/readXhsProfileWithBridge.ts +0 -227
  133. package/integrations/xhs-reader/client/xhsWebApiClient.ts +0 -229
  134. package/integrations/xhs-reader/normalize.ts +0 -390
  135. package/integrations/xhs-reader/orchestrator.ts +0 -178
  136. package/taskRunCommand.ts +0 -265
@@ -1,6 +1,7 @@
1
1
  // 文件: render/table/types.ts
2
2
 
3
3
  import { DataType } from "../../create/types";
4
+ import type { ContentIcon } from "../contentIcon/types";
4
5
 
5
6
  /* --------------------------------------------------------------------------
6
7
  * 1. 列类型(TableColumn)
@@ -214,6 +215,15 @@ export interface TableMeta {
214
215
  /** 表显示名(可中文) */
215
216
  displayName?: string;
216
217
 
218
+ /** 表格自定义图标;未设置时 UI 使用默认表格图标。 */
219
+ icon?: ContentIcon | null;
220
+
221
+ /**
222
+ * 机器可读用途,用于在当前用户/space 范围内查找某类表。
223
+ * 例如 agent_eval_workbench。
224
+ */
225
+ purpose?: string;
226
+
217
227
  /**
218
228
  * 表用途说明,给人和 AI 都看的版本:
219
229
  * 建议包括:
@@ -2,15 +2,12 @@ import { createScriptCommand, type CommandEntry } from "./cliCommandTypes";
2
2
 
3
3
  function getDocScriptCommands(): CommandEntry[] {
4
4
  return [
5
- createScriptCommand(["doc", "create"], "createDoc.ts", "Create a normal doc"),
6
5
  createScriptCommand(["doc", "read"], "readDoc.ts", "Read a normal doc"),
7
6
  createScriptCommand(["doc", "update"], "updateDoc.ts", "Update a normal doc"),
8
7
  createScriptCommand(["doc", "delete"], "deleteDoc.ts", "Delete a normal doc"),
9
- createScriptCommand(["skill-doc", "create"], "createSkillDoc.ts", "Create a skill doc"),
10
8
  createScriptCommand(["skill-doc", "read"], "readSkillDoc.ts", "Read a skill doc"),
11
9
  createScriptCommand(["skill-doc", "update"], "updateSkillDoc.ts", "Update a skill doc"),
12
10
  createScriptCommand(["skill-doc", "delete"], "deleteSkillDoc.ts", "Delete a skill doc"),
13
- createScriptCommand(["skill", "create-doc"], "createSkillDoc.ts", "Create a skill-backed doc"),
14
11
  ];
15
12
  }
16
13
 
package/secretScan.ts ADDED
@@ -0,0 +1,31 @@
1
+ export type SecretFinding = {
2
+ label: string;
3
+ line: number;
4
+ preview: string;
5
+ };
6
+
7
+ const SECRET_PATTERNS: Array<{ label: string; pattern: RegExp }> = [
8
+ { label: "password", pattern: /\bpassword\s*[:=]\s*["']?[^"',\s#]{8,}/i },
9
+ { label: "psk", pattern: /\bpsk\s*[:=]\s*["']?[^"',\s#]{8,}/i },
10
+ { label: "api key", pattern: /\b(api[_-]?key|apikey)\s*[:=]\s*["']?[^"',\s#]{12,}/i },
11
+ { label: "token", pattern: /\b(token|secret)\s*[:=]\s*["']?[^"',\s#]{12,}/i },
12
+ { label: "private key", pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----/ },
13
+ ];
14
+
15
+ export function findPotentialSecrets(text: string): SecretFinding[] {
16
+ return text
17
+ .split(/\r?\n/)
18
+ .flatMap((line, index) =>
19
+ SECRET_PATTERNS.filter(({ pattern }) => pattern.test(line)).map(({ label }) => ({
20
+ label,
21
+ line: index + 1,
22
+ preview: line.length > 120 ? `${line.slice(0, 117)}...` : line,
23
+ }))
24
+ );
25
+ }
26
+
27
+ export function formatSecretFindings(findings: SecretFinding[]) {
28
+ return findings
29
+ .map((finding) => ` line ${finding.line} (${finding.label}): ${finding.preview}`)
30
+ .join("\n");
31
+ }
@@ -16,25 +16,32 @@ import type { AgentRuntimeOptions } from "../../../ai/agent/types";
16
16
  export type ChatMessage = AgentRuntimeChatMessage;
17
17
 
18
18
  export type AssistantToolCall = AgentRuntimeToolCall;
19
-
19
+
20
+ export type AgentRunUserInputReferencePart = {
21
+ type: string;
22
+ name?: string;
23
+ pageKey?: string;
24
+ dialogKey?: string;
25
+ };
26
+
27
+ export type AgentRunUserInputPart =
28
+ | { type: "text"; text: string }
29
+ | {
30
+ type: "image_url";
31
+ image_url: { url: string };
32
+ google_native?: {
33
+ inlineData?: {
34
+ mimeType: string;
35
+ data: string;
36
+ };
37
+ thoughtSignature?: string;
38
+ };
39
+ }
40
+ | AgentRunUserInputReferencePart;
41
+
20
42
  export interface AgentRunRequest {
21
43
  agentKey: string;
22
- userInput:
23
- | string
24
- | Array<
25
- | { type: "text"; text: string }
26
- | {
27
- type: "image_url";
28
- image_url: { url: string };
29
- google_native?: {
30
- inlineData: {
31
- mimeType: string;
32
- data: string;
33
- };
34
- thoughtSignature?: string;
35
- };
36
- }
37
- >;
44
+ userInput: string | AgentRunUserInputPart[];
38
45
  messages?: ChatMessage[]; // 历史对话,调用方自行管理(不持久化)
39
46
  runtimeContext?: AgentRunRuntimeContext;
40
47
  stream?: boolean;
@@ -61,12 +68,6 @@ export interface AgentRunRuntimeContext {
61
68
  runtime?: string;
62
69
  entrypoint?: string;
63
70
  capabilities?: string[];
64
- taskRun?: {
65
- rowDbKey?: string;
66
- taskRunId?: string;
67
- workItemId?: string;
68
- artifactIds?: string[];
69
- };
70
71
  subjectRefs?: DialogSubjectRef[];
71
72
  }
72
73
 
package/tableCommands.ts CHANGED
@@ -55,6 +55,11 @@ function parseOutputMode(raw: string): OutputMode {
55
55
  throw new Error(`--output must be one of full, raw, items, json, jsonl; got ${raw}`);
56
56
  }
57
57
 
58
+ function resolveQueryColumns(columns: string[] | undefined, includeActivity: boolean): string[] | undefined {
59
+ if (!includeActivity) return columns;
60
+ return Array.isArray(columns) ? includeTableActivityColumns(columns) : undefined;
61
+ }
62
+
58
63
  function parseTableArg(raw: string): { tenantId?: string; tableId?: string } {
59
64
  const trimmed = raw.trim();
60
65
  if (!trimmed) return {};
@@ -66,6 +71,32 @@ function parseTableArg(raw: string): { tenantId?: string; tableId?: string } {
66
71
  };
67
72
  }
68
73
 
74
+ function normalizeSpaceInput(raw: string): string {
75
+ const value = raw.trim();
76
+ if (!value) return "";
77
+ if (value.startsWith("http://") || value.startsWith("https://")) {
78
+ const match = new URL(value).pathname.match(/^\/space\/([^/]+)/);
79
+ return match?.[1] ? decodeURIComponent(match[1]).replace(/^space-/, "") : "";
80
+ }
81
+ return value.replace(/^space-/, "");
82
+ }
83
+
84
+ function getSpaceContentKeys(spaceRecord: any): Set<string> {
85
+ const keys = new Set<string>();
86
+ const contents = spaceRecord?.contents;
87
+ if (!contents || typeof contents !== "object") return keys;
88
+ for (const [entryKey, value] of Object.entries(contents)) {
89
+ keys.add(entryKey);
90
+ if (value && typeof value === "object") {
91
+ const contentKey = (value as any).contentKey;
92
+ if (typeof contentKey === "string" && contentKey.trim()) {
93
+ keys.add(contentKey.trim());
94
+ }
95
+ }
96
+ }
97
+ return keys;
98
+ }
99
+
69
100
  function resolveServerUrl(args: string[], env: EnvLike): string {
70
101
  return (
71
102
  readOption(args, "--server-url") ||
@@ -89,16 +120,46 @@ function resolveAuthToken(args: string[], env: EnvLike): string {
89
120
  }
90
121
 
91
122
  function parseJwtTenantId(token: string): string | undefined {
92
- const [, payload] = token.split(".");
93
- if (!payload) return undefined;
94
- try {
95
- const normalized = payload.replace(/-/g, "+").replace(/_/g, "/");
96
- const decoded = Buffer.from(normalized, "base64").toString("utf8");
97
- const parsed = JSON.parse(decoded);
98
- return typeof parsed.userId === "string" ? parsed.userId : typeof parsed.sub === "string" ? parsed.sub : undefined;
99
- } catch {
100
- return undefined;
123
+ for (const parsed of parseJwtPayloadCandidates(token)) {
124
+ const userId = parsed.userId;
125
+ const sub = parsed.sub;
126
+ if (typeof userId === "string" && userId) return userId;
127
+ if (typeof sub === "string" && sub) return sub;
101
128
  }
129
+ return undefined;
130
+ }
131
+
132
+ function parseJwtUsername(token: string): string | undefined {
133
+ for (const parsed of parseJwtPayloadCandidates(token)) {
134
+ const username = parsed.username;
135
+ if (typeof username === "string" && username.trim()) return username.trim();
136
+ }
137
+ return undefined;
138
+ }
139
+
140
+ function parseJwtPayloadCandidates(token: string): Array<Record<string, unknown>> {
141
+ const trimmed = token.trim();
142
+ if (!trimmed) return [];
143
+ const parts = trimmed.split(".").filter(Boolean);
144
+ const payloadCandidates = parts.length >= 3 ? [parts[1], parts[0]] : [parts[0]];
145
+ const parsed: Array<Record<string, unknown>> = [];
146
+ for (const payload of payloadCandidates) {
147
+ try {
148
+ const decoded = Buffer.from(payload, "base64").toString("utf8");
149
+ const value = JSON.parse(decoded);
150
+ if (value && typeof value === "object" && !Array.isArray(value)) {
151
+ parsed.push(value as Record<string, unknown>);
152
+ }
153
+ } catch {}
154
+ }
155
+ return parsed;
156
+ }
157
+
158
+ function resolveUserId(args: string[], env: EnvLike, authToken: string): string | undefined {
159
+ const explicit = readOption(args, "--tenant-id") || env.USER_ID || parseJwtTenantId(authToken);
160
+ if (explicit) return explicit;
161
+ const username = parseJwtUsername(authToken);
162
+ return username && /^[a-z0-9_-]+$/i.test(username) ? username : undefined;
102
163
  }
103
164
 
104
165
  function formatOutput(input: { envelope: any; rawData: any; mode: OutputMode }): string {
@@ -133,6 +194,182 @@ function usage(): string {
133
194
  ].join("\n");
134
195
  }
135
196
 
197
+ function listUsage(): string {
198
+ return [
199
+ "Usage:",
200
+ " nolo table list [--limit <n>] [--title-query <text>] [--purpose <purpose>] [--output full|raw|items|json|jsonl]",
201
+ "",
202
+ ].join("\n");
203
+ }
204
+
205
+ function tableHasPurpose(table: any, purpose: string): boolean {
206
+ return typeof table?.purpose === "string" && table.purpose.toLowerCase() === purpose.toLowerCase();
207
+ }
208
+
209
+ function getComparableUpdatedAt(record: any): number {
210
+ const raw = record?.updatedAt ?? record?.updated_at ?? record?.createdAt ?? record?.created;
211
+ if (typeof raw === "number" && Number.isFinite(raw)) return raw;
212
+ if (typeof raw === "string") return Date.parse(raw) || 0;
213
+ return 0;
214
+ }
215
+
216
+ function formatTableListOutput(args: {
217
+ mode: OutputMode;
218
+ tables: any[];
219
+ envelope: any;
220
+ }) {
221
+ if (args.mode === "full") return JSON.stringify(args.envelope, null, 2);
222
+ if (args.mode === "raw") return JSON.stringify(args.tables, null, 2);
223
+ if (args.mode === "items") return JSON.stringify(args.tables, null, 2);
224
+ return args.tables.map((item) => JSON.stringify(item)).join("\n");
225
+ }
226
+
227
+ export async function runTableListCommand(args: string[], deps: TableCommandDeps = {}): Promise<number> {
228
+ const env = deps.env ?? process.env;
229
+ const output = deps.output ?? process.stdout;
230
+ const fetchImpl = deps.fetchImpl ?? fetch;
231
+ if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
232
+ output.write(listUsage());
233
+ return 0;
234
+ }
235
+
236
+ let outputMode: OutputMode;
237
+ try {
238
+ outputMode = parseOutputMode(readOption(args, "--output") || "items");
239
+ } catch (error) {
240
+ output.write(`[nolo] table list failed: ${error instanceof Error ? error.message : String(error)}\n`);
241
+ return 1;
242
+ }
243
+
244
+ const authToken = resolveAuthToken(args, env);
245
+ const userId = resolveUserId(args, env, authToken);
246
+ if (!userId) {
247
+ output.write("[nolo] table list failed: user id is required. Pass --tenant-id or use a login token.\n");
248
+ return 1;
249
+ }
250
+ if (!authToken) {
251
+ output.write("[nolo] table list failed: AUTH_TOKEN is required.\n");
252
+ return 1;
253
+ }
254
+
255
+ const limit = Math.max(1, Math.min(Number(readOption(args, "--limit") || 50), 200));
256
+ const titleQuery = readOption(args, "--title-query").trim().toLowerCase();
257
+ const purpose = readOption(args, "--purpose").trim();
258
+ const spaceId = normalizeSpaceInput(readOption(args, "--space") || readOption(args, "--space-id"));
259
+ const scanLimit = titleQuery || purpose ? Math.max(limit * 5, 200) : limit;
260
+ const serverUrl = resolveServerUrl(args, env);
261
+
262
+ let records: any[];
263
+ try {
264
+ records = spaceId
265
+ ? await fetchSpaceTableRecords({
266
+ authToken,
267
+ fetchImpl,
268
+ serverUrl,
269
+ spaceId,
270
+ })
271
+ : await fetchUserTableRecords({
272
+ authToken,
273
+ fetchImpl,
274
+ limit: Math.min(scanLimit, 500),
275
+ serverUrl,
276
+ userId,
277
+ });
278
+ } catch (error) {
279
+ output.write(`[nolo] table list failed: ${error instanceof Error ? error.message : String(error)}\n`);
280
+ return 1;
281
+ }
282
+ const tables = records
283
+ .filter((table: any) => table && typeof table === "object" && !table.deletedAt)
284
+ .filter((table: any) => !titleQuery || String(table.displayName ?? table.name ?? table.title ?? "").toLowerCase().includes(titleQuery))
285
+ .filter((table: any) => !purpose || tableHasPurpose(table, purpose))
286
+ .sort((left: any, right: any) => getComparableUpdatedAt(right) - getComparableUpdatedAt(left))
287
+ .slice(0, limit)
288
+ .map((table: any) => ({
289
+ dbKey: table.dbKey ?? null,
290
+ tableId: table.tableId ?? null,
291
+ displayName: table.displayName ?? table.name ?? table.title ?? "(untitled)",
292
+ spaceId: table.spaceId ?? null,
293
+ tenantId: table.tenantId ?? null,
294
+ columnCount: Array.isArray(table.columns) ? table.columns.length : 0,
295
+ createdAt: table.createdAt ?? null,
296
+ updatedAt: table.updatedAt ?? table.updated_at ?? null,
297
+ }));
298
+
299
+ const envelope = { success: true, userId, ...(spaceId ? { space: spaceId } : {}), total: tables.length, tables };
300
+ output.write(`${formatTableListOutput({ mode: outputMode, tables, envelope })}\n`);
301
+ return 0;
302
+ }
303
+
304
+ async function readJsonResponse(response: Response) {
305
+ const text = await response.text();
306
+ let payload: any;
307
+ try {
308
+ payload = text ? JSON.parse(text) : {};
309
+ } catch {
310
+ if (!response.ok) throw new Error(text || response.statusText);
311
+ return text;
312
+ }
313
+ if (!response.ok || payload?.error) {
314
+ throw new Error(payload?.error ?? response.statusText);
315
+ }
316
+ return payload;
317
+ }
318
+
319
+ async function fetchUserTableRecords(args: {
320
+ authToken: string;
321
+ fetchImpl: typeof fetch;
322
+ limit: number;
323
+ serverUrl: string;
324
+ userId: string;
325
+ }) {
326
+ const response = await args.fetchImpl(
327
+ `${args.serverUrl}/api/v1/db/query/${encodeURIComponent(args.userId)}?limit=${args.limit}`,
328
+ {
329
+ method: "POST",
330
+ headers: {
331
+ Authorization: `Bearer ${args.authToken}`,
332
+ "Content-Type": "application/json",
333
+ },
334
+ body: JSON.stringify({ type: "table" }),
335
+ }
336
+ );
337
+ const payload = await readJsonResponse(response);
338
+ return Array.isArray(payload?.data?.data)
339
+ ? payload.data.data
340
+ : Array.isArray(payload?.data)
341
+ ? payload.data
342
+ : [];
343
+ }
344
+
345
+ async function fetchSpaceTableRecords(args: {
346
+ authToken: string;
347
+ fetchImpl: typeof fetch;
348
+ serverUrl: string;
349
+ spaceId: string;
350
+ }) {
351
+ const readRecord = async (dbKey: string) => {
352
+ const response = await args.fetchImpl(
353
+ `${args.serverUrl}/api/v1/db/read/${encodeURIComponent(dbKey)}`,
354
+ { headers: { Authorization: `Bearer ${args.authToken}` } }
355
+ );
356
+ const payload = await readJsonResponse(response);
357
+ return payload?.data ?? payload;
358
+ };
359
+ const space = await readRecord(`space-${args.spaceId}`);
360
+ const tableKeys = [...getSpaceContentKeys(space)].filter((key) => key.startsWith("meta-"));
361
+ const records = await Promise.all(
362
+ tableKeys.map(async (key) => {
363
+ try {
364
+ return await readRecord(key);
365
+ } catch {
366
+ return null;
367
+ }
368
+ })
369
+ );
370
+ return records.filter(Boolean);
371
+ }
372
+
136
373
  export async function runTableQueryCommand(args: string[], deps: TableCommandDeps = {}): Promise<number> {
137
374
  const env = deps.env ?? process.env;
138
375
  const output = deps.output ?? process.stdout;
@@ -159,7 +396,7 @@ export async function runTableQueryCommand(args: string[], deps: TableCommandDep
159
396
 
160
397
  const tableArg = parseTableArg(readOption(args, "--table"));
161
398
  const authToken = resolveAuthToken(args, env);
162
- const tenantId = readOption(args, "--tenant-id") || tableArg.tenantId || env.USER_ID || parseJwtTenantId(authToken);
399
+ const tenantId = readOption(args, "--tenant-id") || tableArg.tenantId || resolveUserId(args, env, authToken);
163
400
  const tableId = tableArg.tableId;
164
401
  const hasSingleRowShortcut = Boolean(readOption(args, "--row") || readOption(args, "--row-id") || readOption(args, "--row-dbkey"));
165
402
  if (!tenantId || !tableId) {
@@ -184,9 +421,7 @@ export async function runTableQueryCommand(args: string[], deps: TableCommandDep
184
421
  tenantId,
185
422
  tableId,
186
423
  filters: filters ?? {},
187
- columns: hasFlag(args, "--include-activity")
188
- ? includeTableActivityColumns(columns)
189
- : columns,
424
+ columns: resolveQueryColumns(columns, hasFlag(args, "--include-activity")),
190
425
  includeBaseFields: !hasFlag(args, "--no-base-fields"),
191
426
  limit: Number(readOption(args, "--limit") || (hasSingleRowShortcut ? 1 : 20)),
192
427
  offset: Number(readOption(args, "--offset") || 0),
@@ -1,11 +1,10 @@
1
1
  import { createEnvCommand } from "./cliCommandFactories";
2
2
  import type { CommandEntry } from "./cliCommandTypes";
3
- import { runTableQueryCommand } from "./tableCommands";
4
- import { runTaskRunCommand } from "./taskRunCommand";
3
+ import { runTableListCommand, runTableQueryCommand } from "./tableCommands";
5
4
 
6
5
  export function getWorkflowInternalCommandEntries(): CommandEntry[] {
7
6
  return [
7
+ createEnvCommand(["table", "list"], "List table metadata", runTableListCommand),
8
8
  createEnvCommand(["table", "query"], "Query table rows", runTableQueryCommand),
9
- createEnvCommand(["task-run"], "Update durable task-run state through the runner bridge", runTaskRunCommand),
10
9
  ];
11
10
  }
@@ -1,112 +0,0 @@
1
- export const taskRunFunctionSchema = {
2
- name: "taskRun",
3
- description:
4
- "读写任务卡里的轻量 task-run 上下文。用于 PM、实现 agent、reviewer 共享 workItems、artifact、blocker、review 状态;不直接执行代码、不固定具体工作流。",
5
- parameters: {
6
- type: "object",
7
- properties: {
8
- action: {
9
- type: "string",
10
- enum: [
11
- "readContext",
12
- "readTaskContext",
13
- "queryReadyWorkItems",
14
- "updateWorkItems",
15
- "upsertWorkItem",
16
- "claimOrDispatch",
17
- "claimWorkItem",
18
- "openDialog",
19
- "recordArtifact",
20
- "markBlocked",
21
- "setBlocker",
22
- "clearBlocker",
23
- "requestReview",
24
- "recordReview",
25
- "submitOutcome",
26
- "completeWorkItem",
27
- "closeWorkItem",
28
- ],
29
- description:
30
- "要执行的 task-run 原语动作。新 agent 优先使用 readTaskContext/queryReadyWorkItems/upsertWorkItem/claimWorkItem/openDialog/setBlocker/clearBlocker/submitOutcome/closeWorkItem;旧动作保留兼容。",
31
- },
32
- tenantId: { type: "string", description: "任务表所属用户/租户 ID。" },
33
- tableId: { type: "string", description: "任务表 ID,例如 NOLOTASKBOARD。" },
34
- rowId: { type: "string", description: "任务卡 rowId。" },
35
- dbKey: { type: "string", description: "任务卡完整 dbKey;有 dbKey 时可不传 rowId。" },
36
- workItems: {
37
- type: "array",
38
- description: "updateWorkItems 使用。每项是一个轻量工作项,可包含 dependsOn、writeScope、ownerAgent、reviewPolicy。",
39
- items: { type: "object" },
40
- },
41
- workItemId: {
42
- type: "string",
43
- description: "claimWorkItem / openDialog / submitOutcome / closeWorkItem 等动作定位的 work item ID。",
44
- },
45
- targetWorkItemId: {
46
- type: "string",
47
- description: "recordReview 使用。明确被 review 的目标 work item ID;未提供时兼容使用 workItemId。",
48
- },
49
- agentKey: { type: "string", description: "claimWorkItem / openDialog 使用。接管或参与该 work item 的 agent key。" },
50
- role: { type: "string", description: "claimWorkItem 使用。agent 在本任务里的角色标签。" },
51
- dialogId: { type: "string", description: "相关 agent 对话 ID。" },
52
- participantAgentKeys: {
53
- type: "array",
54
- items: { type: "string" },
55
- description: "openDialog 使用。参与该对话的 agent key 列表。",
56
- },
57
- purpose: {
58
- type: "string",
59
- description: "openDialog 使用。对话目的,例如 assignment/review/status/question/handoff。",
60
- },
61
- message: {
62
- type: "string",
63
- description: "openDialog 使用。给目标 agent 的简短消息。",
64
- },
65
- artifact: {
66
- type: "object",
67
- description: "recordArtifact 使用。包含 kind/ref/summary,可选 producerRunId/workItemId。",
68
- },
69
- artifactIds: {
70
- type: "array",
71
- items: { type: "string" },
72
- description: "requestReview / recordReview / submitOutcome 使用。被 review 或提交的 artifact id 列表。",
73
- },
74
- blocker: {
75
- type: "object",
76
- description: "setBlocker/markBlocked 使用。包含 layer/code/message,可选 evidence。",
77
- },
78
- blockerId: {
79
- type: "string",
80
- description: "clearBlocker 使用。要清除的 blocker id。",
81
- },
82
- reviewerAgentKey: {
83
- type: "string",
84
- description:
85
- "requestReview / recordReview 使用。Reviewer agent key。requestReview 配合 workItemId 时会创建 review work item 和 review dialog link。",
86
- },
87
- reviewStatus: {
88
- type: "string",
89
- enum: ["passed", "needs_changes", "blocked"],
90
- description: "recordReview 使用。",
91
- },
92
- findings: {
93
- type: "array",
94
- items: { type: "object" },
95
- description: "recordReview 使用。review findings,包含 priority/summary/file/line/workItemId。",
96
- },
97
- summary: {
98
- type: "string",
99
- description: "submitOutcome / closeWorkItem 使用。对本次提交或关闭的简短总结。",
100
- },
101
- reason: {
102
- type: "string",
103
- description: "可选。说明本次状态变更原因;submitOutcome 未提供 summary 时可作为 summary。",
104
- },
105
- },
106
- required: ["action"],
107
- },
108
- };
109
-
110
- export async function taskRunFunc(): Promise<never> {
111
- throw new Error("taskRun is a server-side agent tool. Use it through /api/agent/run.");
112
- }
@@ -1,27 +0,0 @@
1
- export type TaskRunPromptContext = {
2
- rowDbKey: string;
3
- taskRunId?: string;
4
- workItemId?: string;
5
- artifactIds?: string[];
6
- };
7
-
8
- export function prependTaskRunPrompt(message: string, context?: TaskRunPromptContext): string {
9
- if (!context?.rowDbKey) return message;
10
- const artifactIds = context.artifactIds?.filter(Boolean) ?? [];
11
- return [
12
- "AI-native task-run context:",
13
- "- This system is AI-native. The current task board is only the present observation and validation use case; different users, companies, and workflows may use different task owners later.",
14
- "- Treat the task row meta as the durable execution context. Do not infer branch, runner, handoff, review, or blocker state from chat history alone.",
15
- `- rowDbKey: ${context.rowDbKey}`,
16
- ...(context.taskRunId ? [`- taskRunId: ${context.taskRunId}`] : []),
17
- ...(context.workItemId ? [`- workItemId: ${context.workItemId}`] : []),
18
- ...(artifactIds.length ? [`- artifactIds: ${artifactIds.join(",")}`] : []),
19
- "- Handoff rule: start from the durable task context, then inspect existing refs such as workItems.dialogIds, workItems.outcome, review.dialogId, activityRefs, agentRuns.dialogId/resultSummary, and artifactRefs. Use dialog read/search for exact evidence.",
20
- "- When you create, update, hand off, block, or review work, update the task context with `bun run task:run -- ...`.",
21
- "- If this turn already includes a server TaskRun preflight snapshot, use that snapshot directly for read-only reporting or planning.",
22
- "- Keep long reasoning and logs in the dialog; record only resumable references in task row meta.",
23
- "",
24
- "User task:",
25
- message,
26
- ].join("\n");
27
- }