@agi-cli/server 0.1.140 → 0.1.141
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/package.json +3 -3
- package/src/events/types.ts +2 -0
- package/src/index.ts +4 -0
- package/src/routes/config/defaults.ts +3 -0
- package/src/routes/config/main.ts +5 -0
- package/src/routes/session-approval.ts +53 -0
- package/src/runtime/message/service.ts +4 -0
- package/src/runtime/provider/index.ts +4 -0
- package/src/runtime/provider/moonshot.ts +8 -0
- package/src/runtime/session/queue.ts +2 -0
- package/src/runtime/stream/step-finish.ts +2 -2
- package/src/runtime/stream/types.ts +1 -1
- package/src/runtime/tools/approval.ts +179 -0
- package/src/runtime/tools/context.ts +2 -0
- package/src/runtime/tools/setup.ts +1 -0
- package/src/tools/adapter.ts +28 -0
- package/sst-env.d.ts +0 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agi-cli/server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.141",
|
|
4
4
|
"description": "HTTP API server for AGI CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"typecheck": "tsc --noEmit"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@agi-cli/sdk": "0.1.
|
|
33
|
-
"@agi-cli/database": "0.1.
|
|
32
|
+
"@agi-cli/sdk": "0.1.141",
|
|
33
|
+
"@agi-cli/database": "0.1.141",
|
|
34
34
|
"drizzle-orm": "^0.44.5",
|
|
35
35
|
"hono": "^4.9.9",
|
|
36
36
|
"zod": "^4.1.8"
|
package/src/events/types.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { registerTerminalsRoutes } from './routes/terminals.ts';
|
|
|
16
16
|
import { registerSessionFilesRoutes } from './routes/session-files.ts';
|
|
17
17
|
import { registerBranchRoutes } from './routes/branch.ts';
|
|
18
18
|
import { registerResearchRoutes } from './routes/research.ts';
|
|
19
|
+
import { registerSessionApprovalRoute } from './routes/session-approval.ts';
|
|
19
20
|
import { registerSolforgeRoutes } from './routes/solforge.ts';
|
|
20
21
|
import type { AgentConfigEntry } from './runtime/agent/registry.ts';
|
|
21
22
|
|
|
@@ -59,6 +60,7 @@ function initApp() {
|
|
|
59
60
|
registerRootRoutes(app);
|
|
60
61
|
registerOpenApiRoute(app);
|
|
61
62
|
registerSessionsRoutes(app);
|
|
63
|
+
registerSessionApprovalRoute(app);
|
|
62
64
|
registerSessionMessagesRoutes(app);
|
|
63
65
|
registerSessionStreamRoute(app);
|
|
64
66
|
registerAskRoutes(app);
|
|
@@ -128,6 +130,7 @@ export function createStandaloneApp(_config?: StandaloneAppConfig) {
|
|
|
128
130
|
registerRootRoutes(honoApp);
|
|
129
131
|
registerOpenApiRoute(honoApp);
|
|
130
132
|
registerSessionsRoutes(honoApp);
|
|
133
|
+
registerSessionApprovalRoute(honoApp);
|
|
131
134
|
registerSessionMessagesRoutes(honoApp);
|
|
132
135
|
registerSessionStreamRoute(honoApp);
|
|
133
136
|
registerAskRoutes(honoApp);
|
|
@@ -225,6 +228,7 @@ export function createEmbeddedApp(config: EmbeddedAppConfig = {}) {
|
|
|
225
228
|
registerRootRoutes(honoApp);
|
|
226
229
|
registerOpenApiRoute(honoApp);
|
|
227
230
|
registerSessionsRoutes(honoApp);
|
|
231
|
+
registerSessionApprovalRoute(honoApp);
|
|
228
232
|
registerSessionMessagesRoutes(honoApp);
|
|
229
233
|
registerSessionStreamRoute(honoApp);
|
|
230
234
|
registerAskRoutes(honoApp);
|
|
@@ -11,6 +11,7 @@ export function registerDefaultsRoute(app: Hono) {
|
|
|
11
11
|
agent?: string;
|
|
12
12
|
provider?: string;
|
|
13
13
|
model?: string;
|
|
14
|
+
toolApproval?: 'auto' | 'dangerous' | 'all';
|
|
14
15
|
scope?: 'global' | 'local';
|
|
15
16
|
}>();
|
|
16
17
|
|
|
@@ -19,11 +20,13 @@ export function registerDefaultsRoute(app: Hono) {
|
|
|
19
20
|
agent: string;
|
|
20
21
|
provider: string;
|
|
21
22
|
model: string;
|
|
23
|
+
toolApproval: 'auto' | 'dangerous' | 'all';
|
|
22
24
|
}> = {};
|
|
23
25
|
|
|
24
26
|
if (body.agent) updates.agent = body.agent;
|
|
25
27
|
if (body.provider) updates.provider = body.provider;
|
|
26
28
|
if (body.model) updates.model = body.model;
|
|
29
|
+
if (body.toolApproval) updates.toolApproval = body.toolApproval;
|
|
27
30
|
|
|
28
31
|
await setConfig(scope, updates, projectRoot);
|
|
29
32
|
|
|
@@ -52,6 +52,11 @@ export function registerMainConfigRoute(app: Hono) {
|
|
|
52
52
|
embeddedConfig?.defaults?.model,
|
|
53
53
|
cfg.defaults.model,
|
|
54
54
|
),
|
|
55
|
+
toolApproval: getDefault(
|
|
56
|
+
undefined,
|
|
57
|
+
embeddedConfig?.defaults?.toolApproval,
|
|
58
|
+
cfg.defaults.toolApproval,
|
|
59
|
+
) as 'auto' | 'dangerous' | 'all',
|
|
55
60
|
};
|
|
56
61
|
|
|
57
62
|
return c.json({
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Hono } from 'hono';
|
|
2
|
+
import {
|
|
3
|
+
resolveApproval,
|
|
4
|
+
getPendingApprovalsForSession,
|
|
5
|
+
} from '../runtime/tools/approval.ts';
|
|
6
|
+
|
|
7
|
+
export function registerSessionApprovalRoute(app: Hono) {
|
|
8
|
+
app.post('/v1/sessions/:id/approval', async (c) => {
|
|
9
|
+
const sessionId = c.req.param('id');
|
|
10
|
+
const body = await c.req.json<{
|
|
11
|
+
callId: string;
|
|
12
|
+
approved: boolean;
|
|
13
|
+
}>();
|
|
14
|
+
|
|
15
|
+
if (!body.callId) {
|
|
16
|
+
return c.json({ ok: false, error: 'callId is required' }, 400);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (typeof body.approved !== 'boolean') {
|
|
20
|
+
return c.json({ ok: false, error: 'approved must be a boolean' }, 400);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log('[approval-route] Received approval request', {
|
|
24
|
+
sessionId,
|
|
25
|
+
callId: body.callId,
|
|
26
|
+
approved: body.approved,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const result = resolveApproval(body.callId, body.approved);
|
|
30
|
+
|
|
31
|
+
if (!result.ok) {
|
|
32
|
+
return c.json(result, 404);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return c.json({ ok: true, callId: body.callId, approved: body.approved });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
app.get('/v1/sessions/:id/approval/pending', async (c) => {
|
|
39
|
+
const sessionId = c.req.param('id');
|
|
40
|
+
const pending = getPendingApprovalsForSession(sessionId);
|
|
41
|
+
|
|
42
|
+
return c.json({
|
|
43
|
+
ok: true,
|
|
44
|
+
pending: pending.map((p) => ({
|
|
45
|
+
callId: p.callId,
|
|
46
|
+
toolName: p.toolName,
|
|
47
|
+
args: p.args,
|
|
48
|
+
messageId: p.messageId,
|
|
49
|
+
createdAt: p.createdAt,
|
|
50
|
+
})),
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
@@ -171,6 +171,9 @@ export async function dispatchAssistantMessage(
|
|
|
171
171
|
);
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
// Read tool approval mode from config
|
|
175
|
+
const toolApprovalMode = cfg.defaults.toolApproval ?? 'auto';
|
|
176
|
+
|
|
174
177
|
enqueueAssistantRun(
|
|
175
178
|
{
|
|
176
179
|
sessionId,
|
|
@@ -184,6 +187,7 @@ export async function dispatchAssistantMessage(
|
|
|
184
187
|
reasoningText,
|
|
185
188
|
isCompactCommand: isCompact,
|
|
186
189
|
compactionContext,
|
|
190
|
+
toolApprovalMode,
|
|
187
191
|
},
|
|
188
192
|
runSessionLoop,
|
|
189
193
|
);
|
|
@@ -6,6 +6,7 @@ import { resolveOpenRouterModel } from './openrouter.ts';
|
|
|
6
6
|
import { resolveSolforgeModel } from './solforge.ts';
|
|
7
7
|
import { getZaiInstance, getZaiCodingInstance } from './zai.ts';
|
|
8
8
|
import { resolveOpencodeModel } from './opencode.ts';
|
|
9
|
+
import { getMoonshotInstance } from './moonshot.ts';
|
|
9
10
|
|
|
10
11
|
export type ProviderName = ProviderId;
|
|
11
12
|
|
|
@@ -42,5 +43,8 @@ export async function resolveModel(
|
|
|
42
43
|
if (provider === 'zai-coding') {
|
|
43
44
|
return getZaiCodingInstance(cfg, model);
|
|
44
45
|
}
|
|
46
|
+
if (provider === 'moonshot') {
|
|
47
|
+
return getMoonshotInstance(cfg, model);
|
|
48
|
+
}
|
|
45
49
|
throw new Error(`Unsupported provider: ${provider}`);
|
|
46
50
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AGIConfig } from '@agi-cli/sdk';
|
|
2
|
+
import { getAuth, createMoonshotModel } from '@agi-cli/sdk';
|
|
3
|
+
|
|
4
|
+
export async function getMoonshotInstance(cfg: AGIConfig, model: string) {
|
|
5
|
+
const auth = await getAuth('moonshot', cfg.projectRoot);
|
|
6
|
+
const apiKey = auth?.type === 'api' ? auth.key : undefined;
|
|
7
|
+
return createMoonshotModel(model, { apiKey });
|
|
8
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ProviderName } from '../provider/index.ts';
|
|
2
2
|
import { publish } from '../../events/bus.ts';
|
|
3
|
+
import type { ToolApprovalMode } from '../tools/approval.ts';
|
|
3
4
|
|
|
4
5
|
export type RunOpts = {
|
|
5
6
|
sessionId: string;
|
|
@@ -14,6 +15,7 @@ export type RunOpts = {
|
|
|
14
15
|
abortSignal?: AbortSignal;
|
|
15
16
|
isCompactCommand?: boolean;
|
|
16
17
|
compactionContext?: string;
|
|
18
|
+
toolApprovalMode?: ToolApprovalMode;
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
export type QueuedMessage = {
|
|
@@ -47,7 +47,7 @@ export function createStepFinishHandler(
|
|
|
47
47
|
try {
|
|
48
48
|
await updateSessionTokensIncrementalFn(
|
|
49
49
|
step.usage,
|
|
50
|
-
step.
|
|
50
|
+
step.providerMetadata,
|
|
51
51
|
opts,
|
|
52
52
|
db,
|
|
53
53
|
);
|
|
@@ -56,7 +56,7 @@ export function createStepFinishHandler(
|
|
|
56
56
|
try {
|
|
57
57
|
await updateMessageTokensIncrementalFn(
|
|
58
58
|
step.usage,
|
|
59
|
-
step.
|
|
59
|
+
step.providerMetadata,
|
|
60
60
|
opts,
|
|
61
61
|
db,
|
|
62
62
|
);
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { publish } from '../../events/bus.ts';
|
|
2
|
+
|
|
3
|
+
export type ToolApprovalMode = 'auto' | 'dangerous' | 'all';
|
|
4
|
+
|
|
5
|
+
export const DANGEROUS_TOOLS = new Set([
|
|
6
|
+
'bash',
|
|
7
|
+
'write',
|
|
8
|
+
'apply_patch',
|
|
9
|
+
'terminal',
|
|
10
|
+
'edit',
|
|
11
|
+
'git_commit',
|
|
12
|
+
'git_push',
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
export const SAFE_TOOLS = new Set([
|
|
16
|
+
'finish',
|
|
17
|
+
'progress_update',
|
|
18
|
+
'update_todos',
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
export interface PendingApproval {
|
|
22
|
+
callId: string;
|
|
23
|
+
toolName: string;
|
|
24
|
+
args: unknown;
|
|
25
|
+
sessionId: string;
|
|
26
|
+
messageId: string;
|
|
27
|
+
resolve: (approved: boolean) => void;
|
|
28
|
+
createdAt: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const pendingApprovals = new Map<string, PendingApproval>();
|
|
32
|
+
|
|
33
|
+
export function requiresApproval(
|
|
34
|
+
toolName: string,
|
|
35
|
+
mode: ToolApprovalMode,
|
|
36
|
+
): boolean {
|
|
37
|
+
if (SAFE_TOOLS.has(toolName)) return false;
|
|
38
|
+
if (mode === 'auto') return false;
|
|
39
|
+
if (mode === 'all') return true;
|
|
40
|
+
if (mode === 'dangerous') return DANGEROUS_TOOLS.has(toolName);
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function requestApproval(
|
|
45
|
+
sessionId: string,
|
|
46
|
+
messageId: string,
|
|
47
|
+
callId: string,
|
|
48
|
+
toolName: string,
|
|
49
|
+
args: unknown,
|
|
50
|
+
timeoutMs = 120000,
|
|
51
|
+
): Promise<boolean> {
|
|
52
|
+
console.log('[approval] requestApproval called', {
|
|
53
|
+
sessionId,
|
|
54
|
+
messageId,
|
|
55
|
+
callId,
|
|
56
|
+
toolName,
|
|
57
|
+
});
|
|
58
|
+
return new Promise((resolve) => {
|
|
59
|
+
const approval: PendingApproval = {
|
|
60
|
+
callId,
|
|
61
|
+
toolName,
|
|
62
|
+
args,
|
|
63
|
+
sessionId,
|
|
64
|
+
messageId,
|
|
65
|
+
resolve,
|
|
66
|
+
createdAt: Date.now(),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
pendingApprovals.set(callId, approval);
|
|
70
|
+
console.log(
|
|
71
|
+
'[approval] Added to pendingApprovals, count:',
|
|
72
|
+
pendingApprovals.size,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
publish({
|
|
76
|
+
type: 'tool.approval.required',
|
|
77
|
+
sessionId,
|
|
78
|
+
payload: {
|
|
79
|
+
callId,
|
|
80
|
+
toolName,
|
|
81
|
+
args,
|
|
82
|
+
messageId,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
setTimeout(() => {
|
|
87
|
+
if (pendingApprovals.has(callId)) {
|
|
88
|
+
pendingApprovals.delete(callId);
|
|
89
|
+
resolve(false);
|
|
90
|
+
publish({
|
|
91
|
+
type: 'tool.approval.resolved',
|
|
92
|
+
sessionId,
|
|
93
|
+
payload: {
|
|
94
|
+
callId,
|
|
95
|
+
toolName,
|
|
96
|
+
approved: false,
|
|
97
|
+
reason: 'timeout',
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}, timeoutMs);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function resolveApproval(
|
|
106
|
+
callId: string,
|
|
107
|
+
approved: boolean,
|
|
108
|
+
): { ok: boolean; error?: string } {
|
|
109
|
+
console.log('[approval] resolveApproval called', {
|
|
110
|
+
callId,
|
|
111
|
+
approved,
|
|
112
|
+
pendingCount: pendingApprovals.size,
|
|
113
|
+
pendingIds: [...pendingApprovals.keys()],
|
|
114
|
+
});
|
|
115
|
+
const approval = pendingApprovals.get(callId);
|
|
116
|
+
if (!approval) {
|
|
117
|
+
console.log('[approval] No pending approval found for callId:', callId);
|
|
118
|
+
return { ok: false, error: 'No pending approval found for this callId' };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
pendingApprovals.delete(callId);
|
|
122
|
+
approval.resolve(approved);
|
|
123
|
+
|
|
124
|
+
publish({
|
|
125
|
+
type: 'tool.approval.resolved',
|
|
126
|
+
sessionId: approval.sessionId,
|
|
127
|
+
payload: {
|
|
128
|
+
callId,
|
|
129
|
+
toolName: approval.toolName,
|
|
130
|
+
approved,
|
|
131
|
+
reason: approved ? 'user_approved' : 'user_rejected',
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return { ok: true };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function getPendingApproval(
|
|
139
|
+
callId: string,
|
|
140
|
+
): PendingApproval | undefined {
|
|
141
|
+
return pendingApprovals.get(callId);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function updateApprovalArgs(callId: string, args: unknown): boolean {
|
|
145
|
+
const approval = pendingApprovals.get(callId);
|
|
146
|
+
if (!approval) return false;
|
|
147
|
+
|
|
148
|
+
approval.args = args;
|
|
149
|
+
|
|
150
|
+
publish({
|
|
151
|
+
type: 'tool.approval.updated',
|
|
152
|
+
sessionId: approval.sessionId,
|
|
153
|
+
payload: {
|
|
154
|
+
callId,
|
|
155
|
+
toolName: approval.toolName,
|
|
156
|
+
args,
|
|
157
|
+
messageId: approval.messageId,
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function getPendingApprovalsForSession(
|
|
165
|
+
sessionId: string,
|
|
166
|
+
): PendingApproval[] {
|
|
167
|
+
return Array.from(pendingApprovals.values()).filter(
|
|
168
|
+
(a) => a.sessionId === sessionId,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function clearPendingApprovalsForSession(sessionId: string): void {
|
|
173
|
+
for (const [callId, approval] of pendingApprovals) {
|
|
174
|
+
if (approval.sessionId === sessionId) {
|
|
175
|
+
approval.resolve(false);
|
|
176
|
+
pendingApprovals.delete(callId);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { eq } from 'drizzle-orm';
|
|
2
2
|
import type { DB } from '@agi-cli/database';
|
|
3
3
|
import { messageParts } from '@agi-cli/database/schema';
|
|
4
|
+
import type { ToolApprovalMode } from './approval.ts';
|
|
4
5
|
import { publish } from '../../events/bus.ts';
|
|
5
6
|
|
|
6
7
|
export type StepExecutionState = {
|
|
@@ -24,6 +25,7 @@ export type ToolAdapterContext = {
|
|
|
24
25
|
stepExecution?: {
|
|
25
26
|
states: Map<number, StepExecutionState>;
|
|
26
27
|
};
|
|
28
|
+
toolApprovalMode?: ToolApprovalMode;
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
export function extractFinishText(input: unknown): string | undefined {
|
|
@@ -32,6 +32,7 @@ export async function setupToolContext(
|
|
|
32
32
|
model: opts.model,
|
|
33
33
|
projectRoot: opts.projectRoot,
|
|
34
34
|
stepExecution: { states: new Map() },
|
|
35
|
+
toolApprovalMode: opts.toolApprovalMode,
|
|
35
36
|
onFirstToolCall: () => {
|
|
36
37
|
if (firstToolSeen) return;
|
|
37
38
|
firstToolSeen = true;
|
package/src/tools/adapter.ts
CHANGED
|
@@ -13,6 +13,10 @@ import {
|
|
|
13
13
|
toClaudeCodeName,
|
|
14
14
|
requiresClaudeCodeNaming,
|
|
15
15
|
} from '../runtime/tools/mapping.ts';
|
|
16
|
+
import {
|
|
17
|
+
requiresApproval,
|
|
18
|
+
requestApproval,
|
|
19
|
+
} from '../runtime/tools/approval.ts';
|
|
16
20
|
|
|
17
21
|
export type { ToolAdapterContext } from '../runtime/tools/context.ts';
|
|
18
22
|
|
|
@@ -33,6 +37,7 @@ type PendingCallMeta = {
|
|
|
33
37
|
startTs: number;
|
|
34
38
|
stepIndex?: number;
|
|
35
39
|
args?: unknown;
|
|
40
|
+
approvalPromise?: Promise<boolean>;
|
|
36
41
|
};
|
|
37
42
|
|
|
38
43
|
function getPendingQueue(
|
|
@@ -294,6 +299,19 @@ export function adaptTools(
|
|
|
294
299
|
toolCallId: callId,
|
|
295
300
|
});
|
|
296
301
|
} catch {}
|
|
302
|
+
// Start approval request with full args
|
|
303
|
+
if (
|
|
304
|
+
ctx.toolApprovalMode &&
|
|
305
|
+
requiresApproval(name, ctx.toolApprovalMode)
|
|
306
|
+
) {
|
|
307
|
+
meta.approvalPromise = requestApproval(
|
|
308
|
+
ctx.sessionId,
|
|
309
|
+
ctx.messageId,
|
|
310
|
+
callId,
|
|
311
|
+
name,
|
|
312
|
+
args,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
297
315
|
if (typeof base.onInputAvailable === 'function') {
|
|
298
316
|
// biome-ignore lint/suspicious/noExplicitAny: AI SDK types are complex
|
|
299
317
|
await base.onInputAvailable(options as any);
|
|
@@ -324,6 +342,16 @@ export function adaptTools(
|
|
|
324
342
|
|
|
325
343
|
const executeWithGuards = async (): Promise<ToolExecuteReturn> => {
|
|
326
344
|
try {
|
|
345
|
+
// Await approval if it was requested in onInputAvailable
|
|
346
|
+
if (meta?.approvalPromise) {
|
|
347
|
+
const approved = await meta.approvalPromise;
|
|
348
|
+
if (!approved) {
|
|
349
|
+
return {
|
|
350
|
+
ok: false,
|
|
351
|
+
error: 'Tool execution rejected by user',
|
|
352
|
+
} as ToolExecuteReturn;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
327
355
|
// Handle session-relative paths and cwd tools
|
|
328
356
|
let res: ToolExecuteReturn | { cwd: string } | null | undefined;
|
|
329
357
|
const cwd = getCwd(ctx.sessionId);
|