nolo-cli 0.1.19 → 0.1.20
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 +9 -1
- package/agent-runtime/agentConfigOptions.ts +12 -0
- package/agent-runtime/agentRecordConfig.ts +99 -0
- package/agent-runtime/agentRecordKeys.ts +14 -0
- package/agent-runtime/dialogMessageRecord.ts +16 -0
- package/agent-runtime/dialogWritePlan.ts +130 -0
- package/agent-runtime/hostAdapter.ts +13 -0
- package/agent-runtime/hybridRecordStore.ts +147 -0
- package/agent-runtime/index.ts +69 -0
- package/agent-runtime/localLoop.ts +69 -5
- package/agent-runtime/localToolPolicy.ts +130 -0
- package/agent-runtime/localWorkspaceTools.ts +1532 -0
- package/agent-runtime/openAiCompatibleProvider.ts +70 -0
- package/agent-runtime/openAiCompatibleProviderConfig.ts +38 -0
- package/agent-runtime/platformChatProvider.ts +241 -0
- package/agent-runtime/taskWorkspace.ts +193 -0
- package/agent-runtime/types.ts +1 -0
- package/agent-runtime/workspaceSession.ts +76 -0
- package/agentAliases.ts +37 -0
- package/agentPullCommand.ts +1 -1
- package/agentRunCommand.ts +278 -52
- package/agentRuntimeCommands.ts +354 -164
- package/agentRuntimeLocal.ts +38 -0
- package/ai/agent/agentSlice.ts +10 -0
- package/ai/agent/buildEditingContext.ts +5 -0
- package/ai/agent/buildSystemPrompt.ts +41 -18
- package/ai/agent/canvasEditingContext.ts +49 -0
- package/ai/agent/cliExecutor.ts +15 -4
- package/ai/agent/createAgentSchema.ts +2 -0
- package/ai/agent/executeToolCall.ts +3 -2
- package/ai/agent/hooks/usePublicAgents.ts +6 -0
- package/ai/agent/pageBuilderHandoffRules.ts +75 -0
- package/ai/agent/runAgentClientLoop.ts +4 -1
- package/ai/agent/runtimeGuidance.ts +19 -0
- package/ai/agent/server/fetchPublicAgents.ts +51 -1
- package/ai/agent/streamAgentChatTurn.ts +20 -2
- package/ai/agent/streamAgentChatTurnUtils.ts +60 -16
- package/ai/chat/accumulateToolCallChunks.ts +40 -9
- package/ai/chat/parseApiError.ts +3 -0
- package/ai/chat/sendOpenAICompletionsRequest.native.ts +23 -10
- package/ai/chat/sendOpenAICompletionsRequest.ts +13 -1
- package/ai/chat/updateTotalUsage.ts +26 -9
- package/ai/llm/deepinfra.ts +51 -0
- package/ai/llm/getPricing.ts +6 -0
- package/ai/llm/kimi.ts +2 -0
- package/ai/llm/openrouterModels.ts +0 -135
- package/ai/llm/providers.ts +1 -0
- package/ai/llm/types.ts +8 -0
- package/ai/taskRun/taskRunProtocol.ts +823 -0
- package/ai/token/calculatePrice.ts +30 -0
- package/ai/token/externalToolCost.ts +49 -29
- package/ai/token/prepareTokenUsageData.ts +6 -1
- package/ai/token/serverTokenWriter.ts +4 -2
- package/ai/tools/agent/agentTools.ts +21 -0
- package/ai/tools/agent/presets/appBuilderPreset.ts +7 -0
- package/ai/tools/agent/streamParallelAgentsTool.ts +2 -1
- package/ai/tools/agent/taskRunTool.ts +112 -0
- package/ai/tools/applyEditTool.ts +6 -3
- package/ai/tools/applyLineEditsTool.ts +6 -3
- package/ai/tools/checkEnvTool.ts +14 -9
- package/ai/tools/codeSearchTool.ts +17 -5
- package/ai/tools/execBashTool.ts +33 -29
- package/ai/tools/fetchWebpageSupport.ts +24 -0
- package/ai/tools/fetchWebpageTool.ts +18 -5
- package/ai/tools/index.ts +158 -0
- package/ai/tools/jdProductScraperTool.ts +821 -0
- package/ai/tools/listFilesTool.ts +6 -3
- package/ai/tools/localFilesTool.ts +200 -0
- package/ai/tools/readFileTool.ts +6 -3
- package/ai/tools/searchRepoTool.ts +6 -3
- package/ai/tools/table/rowTools.ts +6 -1
- package/ai/tools/taobaoTmallProductScraperTool.ts +49 -0
- package/ai/tools/toolApiClient.ts +20 -6
- package/ai/tools/wereadGatewayTool.ts +152 -0
- package/ai/tools/writeFileTool.ts +6 -3
- package/client/agentConfigResolver.test.ts +70 -0
- package/client/agentConfigResolver.ts +1 -0
- package/client/agentRun.test.ts +361 -7
- package/client/agentRun.ts +449 -63
- package/client/hybridRecordStore.test.ts +115 -0
- package/client/hybridRecordStore.ts +41 -0
- package/client/localAgentRecords.test.ts +27 -0
- package/client/localAgentRecords.ts +7 -0
- package/client/localDialogRecords.test.ts +124 -0
- package/client/localDialogRecords.ts +30 -0
- package/client/localProviderResolver.test.ts +78 -0
- package/client/localProviderResolver.ts +1 -0
- package/client/localRuntimeAdapter.test.ts +621 -9
- package/client/localRuntimeAdapter.ts +275 -250
- package/client/localRuntimeDryRun.test.ts +116 -0
- package/client/localToolPolicy.ts +8 -81
- package/client/taskRunPrompt.ts +26 -0
- package/client/taskWorktree.ts +8 -0
- package/client/workspaceSession.test.ts +57 -0
- package/client/workspaceSession.ts +11 -0
- package/commandRegistry.ts +23 -6
- package/connectorRunArtifact.ts +121 -0
- package/database/actions/write.ts +16 -2
- package/database/hooks/useUserData.ts +9 -3
- package/database/server/dataHandlers.ts +18 -20
- package/database/server/emailRepository.ts +3 -3
- package/database/server/patch.ts +18 -10
- package/database/server/query.ts +43 -4
- package/database/server/read.ts +24 -38
- package/database/server/recordIdentity.ts +100 -0
- package/database/server/write.ts +21 -25
- package/index.ts +70 -33
- package/machineCommands.ts +318 -144
- package/package.json +4 -1
- package/tableCommands.ts +181 -0
- package/taskRunCommand.ts +237 -0
|
@@ -111,6 +111,22 @@ const scaleModelInputOutputPrice = (
|
|
|
111
111
|
output: price.output * multiplier,
|
|
112
112
|
});
|
|
113
113
|
|
|
114
|
+
const scaleModelServiceTierPrice = (
|
|
115
|
+
price: ModelPrice,
|
|
116
|
+
inputOutputMultiplier: number,
|
|
117
|
+
cacheMultiplier?: number
|
|
118
|
+
): ModelPrice => ({
|
|
119
|
+
...price,
|
|
120
|
+
input: price.input * inputOutputMultiplier,
|
|
121
|
+
output: price.output * inputOutputMultiplier,
|
|
122
|
+
...(typeof cacheMultiplier === "number" && typeof price.cachingWrite === "number"
|
|
123
|
+
? { cachingWrite: price.cachingWrite * cacheMultiplier }
|
|
124
|
+
: {}),
|
|
125
|
+
...(typeof cacheMultiplier === "number" && typeof price.cachingRead === "number"
|
|
126
|
+
? { cachingRead: price.cachingRead * cacheMultiplier }
|
|
127
|
+
: {}),
|
|
128
|
+
});
|
|
129
|
+
|
|
114
130
|
const resolveGoogleServiceTierPrice = (
|
|
115
131
|
model: Model,
|
|
116
132
|
price: ModelPrice,
|
|
@@ -128,6 +144,20 @@ const resolveGoogleServiceTierPrice = (
|
|
|
128
144
|
typeof imageOutputPrice === "number"
|
|
129
145
|
? { ...price, output: imageOutputPrice }
|
|
130
146
|
: price;
|
|
147
|
+
const serviceTierMultiplier =
|
|
148
|
+
normalizedTier === "batch" ||
|
|
149
|
+
normalizedTier === "flex" ||
|
|
150
|
+
normalizedTier === "priority"
|
|
151
|
+
? model.serviceTierPriceMultipliers?.[normalizedTier]
|
|
152
|
+
: undefined;
|
|
153
|
+
|
|
154
|
+
if (serviceTierMultiplier) {
|
|
155
|
+
return scaleModelServiceTierPrice(
|
|
156
|
+
priceWithImageOutput,
|
|
157
|
+
serviceTierMultiplier.inputOutput,
|
|
158
|
+
serviceTierMultiplier.cache
|
|
159
|
+
);
|
|
160
|
+
}
|
|
131
161
|
|
|
132
162
|
if (normalizedTier === "flex" || normalizedTier === "batch") {
|
|
133
163
|
return scaleModelInputOutputPrice(priceWithImageOutput, 0.5);
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
* chargeExternalTool({ userId, toolId, ... })。
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { ulid } from "ulid";
|
|
15
|
-
import { format } from "date-fns";
|
|
16
|
-
import serverDb from "database/server/db";
|
|
17
|
-
import { createKey, createTokenKey, createTokenStatsKey } from "database/keys";
|
|
18
|
-
import {
|
|
19
|
-
import { DataType } from "create/types";
|
|
20
|
-
import pino from "pino";
|
|
14
|
+
import { ulid } from "ulid";
|
|
15
|
+
import { format } from "date-fns";
|
|
16
|
+
import serverDb from "database/server/db";
|
|
17
|
+
import { createKey, createTokenKey, createTokenStatsKey } from "database/keys";
|
|
18
|
+
import { chargeTokenUsageWithLedger } from "auth/server/tokenUsageBilling";
|
|
19
|
+
import { DataType } from "create/types";
|
|
20
|
+
import pino from "pino";
|
|
21
21
|
import { applyServerDialogProjectionDelta } from "./serverDialogProjection";
|
|
22
22
|
|
|
23
23
|
const logger = pino({ name: "external-tool-cost" });
|
|
@@ -219,23 +219,42 @@ export async function chargeExternalTool(
|
|
|
219
219
|
|
|
220
220
|
cost = Number(cost.toFixed(6));
|
|
221
221
|
if (cost <= 0) return { cost: 0, skipped: true, reason: "zero_cost" };
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
await
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
userId,
|
|
230
|
-
toolId,
|
|
231
|
-
dialogId,
|
|
222
|
+
|
|
223
|
+
const deductReason = reason ?? toolId;
|
|
224
|
+
// 写 usage 记录(与 LLM token 记录同格式,显示在 Usage 页面)
|
|
225
|
+
const usageRecord = await writeUsageRecord({
|
|
226
|
+
userId,
|
|
227
|
+
toolId,
|
|
228
|
+
dialogId,
|
|
232
229
|
inputTokens,
|
|
233
230
|
outputTokens,
|
|
234
|
-
cost,
|
|
235
|
-
provider: pricing.provider,
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
|
|
231
|
+
cost,
|
|
232
|
+
provider: pricing.provider,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const chargeResult = await chargeTokenUsageWithLedger({
|
|
236
|
+
store: serverDb,
|
|
237
|
+
userId,
|
|
238
|
+
tokenKey: usageRecord.recordKey,
|
|
239
|
+
tokenRecord: usageRecord.tokenRecord,
|
|
240
|
+
reason: deductReason,
|
|
241
|
+
txId: `token-${usageRecord.recordKey}`,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
if (!chargeResult.success) {
|
|
245
|
+
logger.warn(
|
|
246
|
+
{
|
|
247
|
+
userId,
|
|
248
|
+
toolId,
|
|
249
|
+
tokenKey: usageRecord.recordKey,
|
|
250
|
+
error: chargeResult.error,
|
|
251
|
+
},
|
|
252
|
+
"External tool split-ledger charge failed",
|
|
253
|
+
);
|
|
254
|
+
return { cost, skipped: true, reason: chargeResult.error ?? "charge_failed" };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (dialogId) {
|
|
239
258
|
try {
|
|
240
259
|
await applyServerDialogProjectionDelta({
|
|
241
260
|
userId,
|
|
@@ -260,7 +279,7 @@ export async function chargeExternalTool(
|
|
|
260
279
|
// 内部:写 usage 记录到 LevelDB
|
|
261
280
|
// ─────────────────────────────────────────
|
|
262
281
|
|
|
263
|
-
async function writeUsageRecord(opts: {
|
|
282
|
+
async function writeUsageRecord(opts: {
|
|
264
283
|
userId: string;
|
|
265
284
|
toolId: string;
|
|
266
285
|
dialogId?: string;
|
|
@@ -268,7 +287,7 @@ async function writeUsageRecord(opts: {
|
|
|
268
287
|
outputTokens: number;
|
|
269
288
|
cost: number;
|
|
270
289
|
provider: string;
|
|
271
|
-
}): Promise<
|
|
290
|
+
}): Promise<{ recordKey: string; tokenRecord: any }> {
|
|
272
291
|
const { userId, toolId, dialogId, inputTokens, outputTokens, cost, provider } = opts;
|
|
273
292
|
const timestamp = Date.now();
|
|
274
293
|
const dateKey = format(timestamp, "yyyy-MM-dd");
|
|
@@ -314,8 +333,9 @@ async function writeUsageRecord(opts: {
|
|
|
314
333
|
models: { ...base.models, [toolId]: inc(base.models?.[toolId]) },
|
|
315
334
|
providers: { ...base.providers, [provider]: inc(base.providers?.[provider]) },
|
|
316
335
|
};
|
|
317
|
-
await serverDb.batch([
|
|
318
|
-
{ type: "put", key: recordKey, value: record },
|
|
319
|
-
{ type: "put", key: statsKey, value: newStats },
|
|
320
|
-
]);
|
|
321
|
-
}
|
|
336
|
+
await serverDb.batch([
|
|
337
|
+
{ type: "put", key: recordKey, value: record },
|
|
338
|
+
{ type: "put", key: statsKey, value: newStats },
|
|
339
|
+
]);
|
|
340
|
+
return { recordKey, tokenRecord: record };
|
|
341
|
+
}
|
|
@@ -4,13 +4,17 @@ import { normalizeUsage } from "./normalizeUsage";
|
|
|
4
4
|
import { resolveBillingTarget } from "./resolveBillingTarget";
|
|
5
5
|
import type { RawUsage, TokenUsageData } from "./types";
|
|
6
6
|
|
|
7
|
+
type SharingLevel = "default" | "split" | "full";
|
|
8
|
+
|
|
7
9
|
interface BillingAgentConfig {
|
|
8
10
|
model: string;
|
|
9
11
|
provider?: string;
|
|
10
12
|
apiSource?: string;
|
|
11
13
|
inputPrice?: number;
|
|
12
14
|
outputPrice?: number;
|
|
15
|
+
sharingLevel?: SharingLevel;
|
|
13
16
|
id?: string;
|
|
17
|
+
userId?: string;
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
interface PrepareTokenUsageDataParams {
|
|
@@ -60,9 +64,10 @@ export const prepareTokenUsageData = ({
|
|
|
60
64
|
? {
|
|
61
65
|
input: agentConfig.inputPrice ?? 0,
|
|
62
66
|
output: agentConfig.outputPrice ?? 0,
|
|
63
|
-
creatorId: extractUserId(agentConfig.id),
|
|
67
|
+
creatorId: agentConfig.userId ?? extractUserId(agentConfig.id),
|
|
64
68
|
}
|
|
65
69
|
: undefined,
|
|
70
|
+
sharingLevel: agentConfig.sharingLevel,
|
|
66
71
|
});
|
|
67
72
|
|
|
68
73
|
return {
|
|
@@ -25,13 +25,15 @@ interface WriteTokenOpts {
|
|
|
25
25
|
apiSource?: string;
|
|
26
26
|
inputPrice?: number;
|
|
27
27
|
outputPrice?: number;
|
|
28
|
+
sharingLevel?: "default" | "split" | "full";
|
|
28
29
|
id?: string;
|
|
30
|
+
userId?: string;
|
|
29
31
|
};
|
|
30
32
|
runId: string; // 对应 dialogId
|
|
31
33
|
rawUsage: RawUsage;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
export async function writeServerTokenRecord(opts: WriteTokenOpts): Promise<{ cost: number }> {
|
|
36
|
+
export async function writeServerTokenRecord(opts: WriteTokenOpts): Promise<{ cost: number; recordKey: string; tokenRecord: any }> {
|
|
35
37
|
const { userId, username = "", agentKey, agentConfig, runId, rawUsage } = opts;
|
|
36
38
|
|
|
37
39
|
const prepared = prepareTokenUsageData({
|
|
@@ -139,5 +141,5 @@ export async function writeServerTokenRecord(opts: WriteTokenOpts): Promise<{ co
|
|
|
139
141
|
}
|
|
140
142
|
|
|
141
143
|
logger.info({ userId, agentKey, cost, model: billedModel, provider: recordProvider }, "Token record written");
|
|
142
|
-
return { cost };
|
|
144
|
+
return { cost, recordKey, tokenRecord };
|
|
143
145
|
}
|
|
@@ -36,6 +36,10 @@ import {
|
|
|
36
36
|
runLlmFunctionSchema,
|
|
37
37
|
runLlmToolFunc,
|
|
38
38
|
} from "./runLlmTool";
|
|
39
|
+
import {
|
|
40
|
+
taskRunFunctionSchema,
|
|
41
|
+
taskRunFunc,
|
|
42
|
+
} from "./taskRunTool";
|
|
39
43
|
|
|
40
44
|
import type { ToolDefinition } from "../index";
|
|
41
45
|
|
|
@@ -173,4 +177,21 @@ export const agentToolDefinitions: ToolDefinition[] = [
|
|
|
173
177
|
behavior: "action",
|
|
174
178
|
uiGroup: "agent",
|
|
175
179
|
},
|
|
180
|
+
{
|
|
181
|
+
id: "taskRun",
|
|
182
|
+
schema: taskRunFunctionSchema,
|
|
183
|
+
executor: taskRunFunc,
|
|
184
|
+
description: {
|
|
185
|
+
name: "taskRun",
|
|
186
|
+
description:
|
|
187
|
+
"读写任务卡里的轻量 task-run 上下文,让 PM、实现 agent、reviewer 共享工作项、交接物、阻塞和 review 状态。",
|
|
188
|
+
category: "计划与编排",
|
|
189
|
+
},
|
|
190
|
+
behavior: "action",
|
|
191
|
+
uiGroup: "agent",
|
|
192
|
+
capability: "general",
|
|
193
|
+
riskLevel: "medium",
|
|
194
|
+
costLevel: "low",
|
|
195
|
+
defaultConsent: "auto",
|
|
196
|
+
},
|
|
176
197
|
];
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* App Builder 预设:面向非技术用户的网站型小应用构建助手配置
|
|
3
3
|
* 用途:在 AgentForm 中预填充,或通过 createAgent 工具一键创建
|
|
4
|
+
*
|
|
5
|
+
* Maintenance note:
|
|
6
|
+
* - The online App Builder agent record is the runtime source of truth.
|
|
7
|
+
* - Keep this preset as a creation/bootstrap template only.
|
|
8
|
+
* - To make App Builder fully online-truth-only, replace consumers of this
|
|
9
|
+
* preset with a read/pull of agent-pub-01APPBUILDER00000001YAII3I and leave
|
|
10
|
+
* only a tiny emergency placeholder here.
|
|
4
11
|
*/
|
|
5
12
|
import { FIREWORKS_KIMI_LATEST_MODEL } from "ai/llm/kimi";
|
|
6
13
|
|
|
@@ -45,7 +45,8 @@ export const streamParallelAgentsFunctionSchema = {
|
|
|
45
45
|
},
|
|
46
46
|
timeoutMs: {
|
|
47
47
|
type: "number",
|
|
48
|
-
description:
|
|
48
|
+
description:
|
|
49
|
+
"可选:单个分支的超时时间(毫秒)。本地 CLI/machine agent 会把该值作为本次 connector 请求预算。",
|
|
49
50
|
},
|
|
50
51
|
budgetCredits: {
|
|
51
52
|
type: "number",
|
|
@@ -0,0 +1,112 @@
|
|
|
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,5 +1,5 @@
|
|
|
1
1
|
import { bumpDevReloadSuppressIfSelfEditing } from "./devReloadUtils";
|
|
2
|
-
import { getToolBaseUrl } from "./toolApiClient";
|
|
2
|
+
import { buildToolRequestHeaders, getToolBaseUrl } from "./toolApiClient";
|
|
3
3
|
|
|
4
4
|
type EditMatchOptions = {
|
|
5
5
|
occurrence?: number;
|
|
@@ -200,7 +200,7 @@ function assertValidArgs(args: ApplyEditArgs): void {
|
|
|
200
200
|
export async function applyEditFunc(
|
|
201
201
|
args: ApplyEditArgs,
|
|
202
202
|
thunkApi: any,
|
|
203
|
-
context?: { parentMessageId?: string; signal?: AbortSignal }
|
|
203
|
+
context?: { parentMessageId?: string; signal?: AbortSignal; agentKey?: string }
|
|
204
204
|
): Promise<{ rawData: any; displayData?: string }> {
|
|
205
205
|
assertValidArgs(args);
|
|
206
206
|
|
|
@@ -217,7 +217,10 @@ export async function applyEditFunc(
|
|
|
217
217
|
const apiUrl = `${baseUrl.replace(/\/+$/, "")}/api/apply-edit`;
|
|
218
218
|
const response = await fetch(apiUrl, {
|
|
219
219
|
method: "POST",
|
|
220
|
-
headers: {
|
|
220
|
+
headers: buildToolRequestHeaders(thunkApi, {
|
|
221
|
+
withAuth: true,
|
|
222
|
+
agentKey: context?.agentKey,
|
|
223
|
+
}),
|
|
221
224
|
signal: context?.signal,
|
|
222
225
|
body: JSON.stringify({ filePath, edits }),
|
|
223
226
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 文件路径: packages/ai/tools/applyLineEditsTool.ts
|
|
2
2
|
|
|
3
3
|
import { bumpDevReloadSuppressIfSelfEditing } from "./devReloadUtils";
|
|
4
|
-
import { getToolBaseUrl } from "./toolApiClient";
|
|
4
|
+
import { buildToolRequestHeaders, getToolBaseUrl } from "./toolApiClient";
|
|
5
5
|
|
|
6
6
|
// ---- Types ----
|
|
7
7
|
|
|
@@ -253,7 +253,7 @@ function assertValidArgs(args: ApplyLineEditsArgs): void {
|
|
|
253
253
|
export async function applyLineEditsFunc(
|
|
254
254
|
args: ApplyLineEditsArgs,
|
|
255
255
|
thunkApi: any,
|
|
256
|
-
context?: { parentMessageId?: string; signal?: AbortSignal }
|
|
256
|
+
context?: { parentMessageId?: string; signal?: AbortSignal; agentKey?: string }
|
|
257
257
|
): Promise<{ rawData: any; displayData?: string }> {
|
|
258
258
|
assertValidArgs(args);
|
|
259
259
|
|
|
@@ -272,7 +272,10 @@ export async function applyLineEditsFunc(
|
|
|
272
272
|
|
|
273
273
|
const response = await fetch(apiUrl, {
|
|
274
274
|
method: "POST",
|
|
275
|
-
headers: {
|
|
275
|
+
headers: buildToolRequestHeaders(thunkApi, {
|
|
276
|
+
withAuth: true,
|
|
277
|
+
agentKey: context?.agentKey,
|
|
278
|
+
}),
|
|
276
279
|
signal: context?.signal,
|
|
277
280
|
body: JSON.stringify({ filePath, edits }),
|
|
278
281
|
});
|
package/ai/tools/checkEnvTool.ts
CHANGED
|
@@ -25,17 +25,22 @@ export const checkEnvFunctionSchema = {
|
|
|
25
25
|
},
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
export async function checkEnvFunc(
|
|
29
|
-
args: CheckEnvArgs,
|
|
30
|
-
thunkApi: any
|
|
31
|
-
|
|
28
|
+
export async function checkEnvFunc(
|
|
29
|
+
args: CheckEnvArgs,
|
|
30
|
+
thunkApi: any,
|
|
31
|
+
context?: { agentKey?: string }
|
|
32
|
+
): Promise<{ rawData: any; displayData: string }> {
|
|
32
33
|
const check = args?.check === "context" ? "context" : "build";
|
|
33
34
|
|
|
34
|
-
const result = await callToolApi<any>(
|
|
35
|
-
thunkApi,
|
|
36
|
-
"/api/check-env",
|
|
37
|
-
{ check }
|
|
38
|
-
|
|
35
|
+
const result = await callToolApi<any>(
|
|
36
|
+
thunkApi,
|
|
37
|
+
"/api/check-env",
|
|
38
|
+
{ check },
|
|
39
|
+
{
|
|
40
|
+
withAuth: true,
|
|
41
|
+
agentKey: context?.agentKey,
|
|
42
|
+
}
|
|
43
|
+
);
|
|
39
44
|
|
|
40
45
|
const ok = !!result?.ok;
|
|
41
46
|
const exitCode = typeof result?.exitCode === "number" ? result.exitCode : -1;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getToolBaseUrl } from "./toolApiClient";
|
|
1
|
+
import { buildToolRequestHeaders, getToolBaseUrl } from "./toolApiClient";
|
|
2
2
|
|
|
3
3
|
export type CodeSearchArgs = {
|
|
4
4
|
query?: string;
|
|
@@ -70,7 +70,7 @@ export const codeSearchFunctionSchema = {
|
|
|
70
70
|
export async function codeSearchFunc(
|
|
71
71
|
args: CodeSearchArgs,
|
|
72
72
|
thunkApi: any,
|
|
73
|
-
context?: { signal?: AbortSignal },
|
|
73
|
+
context?: { signal?: AbortSignal; agentKey?: string },
|
|
74
74
|
): Promise<{ rawData: any; displayData?: string }> {
|
|
75
75
|
const baseUrl = getToolBaseUrl(thunkApi);
|
|
76
76
|
const apiUrl = `${baseUrl}/api/code-search`;
|
|
@@ -78,7 +78,10 @@ export async function codeSearchFunc(
|
|
|
78
78
|
|
|
79
79
|
const response = await fetch(apiUrl, {
|
|
80
80
|
method: "POST",
|
|
81
|
-
headers: {
|
|
81
|
+
headers: buildToolRequestHeaders(thunkApi, {
|
|
82
|
+
withAuth: true,
|
|
83
|
+
agentKey: context?.agentKey,
|
|
84
|
+
}),
|
|
82
85
|
signal: context?.signal,
|
|
83
86
|
body: JSON.stringify({
|
|
84
87
|
query: args.query,
|
|
@@ -90,9 +93,18 @@ export async function codeSearchFunc(
|
|
|
90
93
|
}),
|
|
91
94
|
});
|
|
92
95
|
|
|
93
|
-
const
|
|
96
|
+
const responseText = await response.text();
|
|
97
|
+
let data: any = null;
|
|
98
|
+
try {
|
|
99
|
+
data = responseText ? JSON.parse(responseText) : {};
|
|
100
|
+
} catch {
|
|
101
|
+
data = null;
|
|
102
|
+
}
|
|
94
103
|
if (!response.ok || data?.error) {
|
|
95
|
-
throw new Error(
|
|
104
|
+
throw new Error(
|
|
105
|
+
data?.error ||
|
|
106
|
+
`codeSearch 请求失败: ${response.status}${responseText ? `. 响应: ${responseText}` : ""}`
|
|
107
|
+
);
|
|
96
108
|
}
|
|
97
109
|
|
|
98
110
|
if (mode === "files") {
|
package/ai/tools/execBashTool.ts
CHANGED
|
@@ -142,18 +142,22 @@ function isDangerousCommand(rawCommand: string): boolean {
|
|
|
142
142
|
return false;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
async function requestExec(
|
|
146
|
-
args: ExecBashRequestArgs,
|
|
147
|
-
thunkApi?: any,
|
|
148
|
-
endpoint = "/api/exec-bash",
|
|
149
|
-
|
|
145
|
+
async function requestExec(
|
|
146
|
+
args: ExecBashRequestArgs,
|
|
147
|
+
thunkApi?: any,
|
|
148
|
+
endpoint = "/api/exec-bash",
|
|
149
|
+
context?: { agentKey?: string },
|
|
150
|
+
): Promise<any> {
|
|
150
151
|
const payload = Object.fromEntries(
|
|
151
152
|
Object.entries(args).filter(([, value]) => value !== undefined)
|
|
152
153
|
);
|
|
153
154
|
|
|
154
|
-
if (thunkApi?.getState) {
|
|
155
|
-
return callToolApi(thunkApi, endpoint, payload
|
|
156
|
-
|
|
155
|
+
if (thunkApi?.getState) {
|
|
156
|
+
return callToolApi(thunkApi, endpoint, payload, {
|
|
157
|
+
withAuth: true,
|
|
158
|
+
agentKey: context?.agentKey,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
157
161
|
|
|
158
162
|
const baseUrl =
|
|
159
163
|
(typeof process !== "undefined" &&
|
|
@@ -309,7 +313,7 @@ export const startExecShellSession = (
|
|
|
309
313
|
"/api/exec-shell",
|
|
310
314
|
);
|
|
311
315
|
|
|
312
|
-
export async function execBashFunc(args: any): Promise<{
|
|
316
|
+
export async function execBashFunc(args: any, thunkApi?: any, context?: { agentKey?: string }): Promise<{
|
|
313
317
|
rawData: any;
|
|
314
318
|
displayData: string;
|
|
315
319
|
}> {
|
|
@@ -327,15 +331,15 @@ export async function execBashFunc(args: any): Promise<{
|
|
|
327
331
|
}
|
|
328
332
|
|
|
329
333
|
try {
|
|
330
|
-
const json = await requestExec({
|
|
331
|
-
...(trimmedCommand ? { command: trimmedCommand } : {}),
|
|
332
|
-
...(cwd ? { cwd } : {}),
|
|
333
|
-
...(interactive ? { interactive: true } : {}),
|
|
334
|
-
...(pty ? { pty: true } : {}),
|
|
335
|
-
...(isSessionFollowup ? { sessionId } : {}),
|
|
336
|
-
...(typeof input === "string" ? { input } : {}),
|
|
337
|
-
...(close ? { close: true } : {}),
|
|
338
|
-
});
|
|
334
|
+
const json = await requestExec({
|
|
335
|
+
...(trimmedCommand ? { command: trimmedCommand } : {}),
|
|
336
|
+
...(cwd ? { cwd } : {}),
|
|
337
|
+
...(interactive ? { interactive: true } : {}),
|
|
338
|
+
...(pty ? { pty: true } : {}),
|
|
339
|
+
...(isSessionFollowup ? { sessionId } : {}),
|
|
340
|
+
...(typeof input === "string" ? { input } : {}),
|
|
341
|
+
...(close ? { close: true } : {}),
|
|
342
|
+
}, thunkApi, "/api/exec-bash", context);
|
|
339
343
|
return formatExecResult("execBash", { cwd }, json, trimmedCommand, sessionId);
|
|
340
344
|
} catch (error: any) {
|
|
341
345
|
const msg = `execBash 调用失败:${error?.message || String(error)}`;
|
|
@@ -343,7 +347,7 @@ export async function execBashFunc(args: any): Promise<{
|
|
|
343
347
|
}
|
|
344
348
|
}
|
|
345
349
|
|
|
346
|
-
export async function execShellFunc(args: any): Promise<{
|
|
350
|
+
export async function execShellFunc(args: any, thunkApi?: any, context?: { agentKey?: string }): Promise<{
|
|
347
351
|
rawData: any;
|
|
348
352
|
displayData: string;
|
|
349
353
|
}> {
|
|
@@ -361,16 +365,16 @@ export async function execShellFunc(args: any): Promise<{
|
|
|
361
365
|
}
|
|
362
366
|
|
|
363
367
|
try {
|
|
364
|
-
const json = await requestExec({
|
|
365
|
-
...(trimmedCommand ? { command: trimmedCommand } : {}),
|
|
366
|
-
...(cwd ? { cwd } : {}),
|
|
367
|
-
...(shell ? { shell } : {}),
|
|
368
|
-
...(interactive ? { interactive: true } : {}),
|
|
369
|
-
...(pty ? { pty: true } : {}),
|
|
370
|
-
...(isSessionFollowup ? { sessionId } : {}),
|
|
371
|
-
...(typeof input === "string" ? { input } : {}),
|
|
372
|
-
...(close ? { close: true } : {}),
|
|
373
|
-
},
|
|
368
|
+
const json = await requestExec({
|
|
369
|
+
...(trimmedCommand ? { command: trimmedCommand } : {}),
|
|
370
|
+
...(cwd ? { cwd } : {}),
|
|
371
|
+
...(shell ? { shell } : {}),
|
|
372
|
+
...(interactive ? { interactive: true } : {}),
|
|
373
|
+
...(pty ? { pty: true } : {}),
|
|
374
|
+
...(isSessionFollowup ? { sessionId } : {}),
|
|
375
|
+
...(typeof input === "string" ? { input } : {}),
|
|
376
|
+
...(close ? { close: true } : {}),
|
|
377
|
+
}, thunkApi, "/api/exec-shell", context);
|
|
374
378
|
return formatExecResult("execShell", { cwd, shell }, json, trimmedCommand, sessionId);
|
|
375
379
|
} catch (error: any) {
|
|
376
380
|
const msg = `execShell 调用失败:${error?.message || String(error)}`;
|
|
@@ -16,6 +16,30 @@ export interface ExtractionIssue {
|
|
|
16
16
|
message: string;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
export function extractAdvertisedMarkdownUrl(markdown: string, sourceUrl: string) {
|
|
20
|
+
if (!markdown.trim()) return null;
|
|
21
|
+
|
|
22
|
+
let source: URL;
|
|
23
|
+
try {
|
|
24
|
+
source = new URL(sourceUrl);
|
|
25
|
+
} catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const hintMatch = markdown.match(/Get this page as Markdown:\s*(https?:\/\/[^\s)]+)/i);
|
|
30
|
+
const rawHint = hintMatch?.[1]?.trim();
|
|
31
|
+
if (!rawHint) return null;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const hinted = new URL(rawHint);
|
|
35
|
+
if (hinted.origin !== source.origin) return null;
|
|
36
|
+
if (!/\.md$/i.test(hinted.pathname)) return null;
|
|
37
|
+
return hinted.toString();
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
19
43
|
function normalizePathname(pathname: string) {
|
|
20
44
|
return pathname
|
|
21
45
|
.replace(/\/index\.md$/i, "/")
|