nolo-cli 0.1.38 → 0.1.40
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 +4 -4
- package/agent-runtime/agentRecordConfig.ts +0 -14
- package/agent-runtime/dialogWritePlan.ts +39 -0
- package/agent-runtime/hostAdapter.ts +4 -1
- package/agent-runtime/hybridRecordStore.ts +1 -1
- package/agent-runtime/index.ts +39 -18
- package/agent-runtime/localDialogRead.ts +23 -0
- package/agent-runtime/localLoop.ts +87 -7
- package/agent-runtime/localToolPolicy.ts +1 -17
- package/agent-runtime/localWorkspaceTools.ts +51 -110
- package/agent-runtime/noloWorkspaceTools.ts +371 -0
- package/agent-runtime/runtimeToolPolicy.ts +3 -14
- package/agent-runtime/runtimeToolSurface.ts +198 -0
- package/agent-runtime/types.ts +2 -1
- package/agentAliases.ts +8 -1
- package/agentRunCommand.ts +65 -95
- package/agentRuntimeLocal.ts +1 -7
- package/ai/agent/cliExecutor.ts +249 -34
- package/chat/messages/fetchMessages.ts +84 -0
- package/chat/messages/types.ts +185 -0
- package/cli/agentAliases.ts +8 -1
- package/cli/agentRunCommand.ts +65 -95
- package/cli/agentRuntimeLocal.ts +1 -7
- package/cli/cliEnvHelpers.ts +11 -7
- package/cli/client/agentRun.ts +94 -93
- package/cli/client/localRuntimeAdapter.ts +250 -69
- package/cli/commandRegistry.ts +2 -2
- package/cli/connectorRunArtifact.ts +0 -1
- package/cli/dialogCommands.ts +572 -0
- package/cli/dialogInternalCommandEntries.ts +4 -0
- package/cli/machineWsRunDispatch.ts +76 -39
- package/cli/offlineMarxistsAgentCommand.ts +6 -4
- package/cli/scriptCommandEntries.ts +0 -2
- package/cli/tui/readlineWorkspace.ts +69 -0
- package/cli/tui/session.ts +176 -1
- package/cliEnvHelpers.ts +11 -7
- package/client/agentConfigResolver.test.ts +4 -5
- package/client/agentRun.test.ts +307 -57
- package/client/agentRun.ts +94 -93
- package/client/localRuntimeAdapter.test.ts +332 -53
- package/client/localRuntimeAdapter.ts +250 -69
- package/client/localRuntimeDryRun.test.ts +23 -9
- package/client/localToolPolicy.test.ts +4 -4
- package/commandRegistry.ts +2 -2
- package/connectorRunArtifact.ts +0 -1
- package/database/server/db.ts +99 -0
- package/database/server/ensureDbOpen.ts +12 -0
- package/database/server/levelAuthorityStore.ts +20 -1
- package/database/server/memoryAuthorityStore.ts +133 -0
- package/database/server/serverStoreFactory.ts +118 -0
- package/dialogCommands.ts +572 -0
- package/dialogInternalCommandEntries.ts +4 -0
- package/machineWsRunDispatch.ts +76 -39
- package/offlineMarxistsAgentCommand.ts +6 -4
- package/package.json +11 -9
- package/scriptCommandEntries.ts +0 -2
- package/tui/readlineWorkspace.ts +69 -0
- package/tui/session.ts +176 -1
- package/agent-runtime/taskWorkspace.ts +0 -193
- package/agent-runtime/workspaceSession.ts +0 -76
- package/cli/client/taskWorktree.ts +0 -8
- package/cli/client/workspaceSession.ts +0 -11
- package/client/taskWorktree.ts +0 -8
- package/client/workspaceSession.test.ts +0 -57
- package/client/workspaceSession.ts +0 -11
package/README.md
CHANGED
|
@@ -178,10 +178,10 @@ nolo dialog list --space <space>
|
|
|
178
178
|
nolo dialog read <dialog>
|
|
179
179
|
nolo dialog delete <dialog> --yes
|
|
180
180
|
nolo doc list --agent <agent>
|
|
181
|
-
nolo table query --table meta-
|
|
182
|
-
nolo table query --table meta-
|
|
183
|
-
nolo table update-row --table meta-
|
|
184
|
-
nolo table add-column --table meta-
|
|
181
|
+
nolo table query --table meta-0e95801d90-NOLOTASKBOARD --limit 20
|
|
182
|
+
nolo table query --table meta-0e95801d90-NOLOTASKBOARD --columns '["title","status","owner","priority","codeStatus"]' --no-base-fields --output items
|
|
183
|
+
nolo table update-row --table meta-0e95801d90-NOLOTASKBOARD --row 01ROWID --changes '{"status":"已完成"}'
|
|
184
|
+
nolo table add-column --table meta-0e95801d90-NOLOTASKBOARD --schema-write-ok --name "blockedBy" --label "Blocked By"
|
|
185
185
|
```
|
|
186
186
|
|
|
187
187
|
`agent list`, `dialog list`, and record delete commands use global server
|
|
@@ -19,18 +19,6 @@ function objectField(record: AgentRecord, key: string): Record<string, unknown>
|
|
|
19
19
|
: undefined;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
function localWorkspaceModeField(record: AgentRecord): "current" | "task-worktree" | undefined {
|
|
23
|
-
const directValue = stringField(record, "localWorkspaceMode");
|
|
24
|
-
const runtimeBinding = objectField(record, "runtimeBinding");
|
|
25
|
-
const runtimeValue = typeof runtimeBinding?.localWorkspaceMode === "string"
|
|
26
|
-
? runtimeBinding.localWorkspaceMode
|
|
27
|
-
: typeof runtimeBinding?.workspaceMode === "string"
|
|
28
|
-
? runtimeBinding.workspaceMode
|
|
29
|
-
: undefined;
|
|
30
|
-
const value = directValue ?? runtimeValue;
|
|
31
|
-
return value === "task-worktree" || value === "current" ? value : undefined;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
22
|
function appendUniqueStrings(values: string[], next: unknown) {
|
|
35
23
|
if (!Array.isArray(next)) return values;
|
|
36
24
|
const seen = new Set(values);
|
|
@@ -84,7 +72,6 @@ export function resolveAgentRuntimeConfigFromRecord(
|
|
|
84
72
|
const runtimeToolPolicy =
|
|
85
73
|
objectField(record, "runtimeToolPolicy") ??
|
|
86
74
|
objectField(runtimeBinding ?? {}, "runtimeToolPolicy");
|
|
87
|
-
const localWorkspaceMode = localWorkspaceModeField(record);
|
|
88
75
|
const delegation = objectField(record, "delegation");
|
|
89
76
|
return {
|
|
90
77
|
key,
|
|
@@ -108,7 +95,6 @@ export function resolveAgentRuntimeConfigFromRecord(
|
|
|
108
95
|
...(reasoningEffort ? { reasoning_effort: reasoningEffort } : {}),
|
|
109
96
|
...(runtimeBinding ? { runtimeBinding } : {}),
|
|
110
97
|
...(runtimeToolPolicy ? { runtimeToolPolicy } : {}),
|
|
111
|
-
...(localWorkspaceMode ? { localWorkspaceMode } : {}),
|
|
112
98
|
...(delegation ? { delegation } : {}),
|
|
113
99
|
rawRecord: record,
|
|
114
100
|
};
|
|
@@ -34,6 +34,28 @@ function resolveDialogTitle(args: {
|
|
|
34
34
|
return lastUserText ? lastUserText.slice(0, 80) : "Local agent run";
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function normalizeNonEmptyString(value: unknown) {
|
|
38
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function buildDialogLineageFields(args: {
|
|
42
|
+
input: AgentRuntimeSaveTurnInput;
|
|
43
|
+
existingDialog?: DialogRecord | null;
|
|
44
|
+
}) {
|
|
45
|
+
const inheritedFromDialogKey = normalizeNonEmptyString(args.input.inheritedFromDialogKey);
|
|
46
|
+
const parentDialogId = normalizeNonEmptyString(args.input.parentDialogId);
|
|
47
|
+
if (!inheritedFromDialogKey && !parentDialogId) return {};
|
|
48
|
+
const rootDialogId =
|
|
49
|
+
normalizeNonEmptyString(args.existingDialog?.rootDialogId) ??
|
|
50
|
+
normalizeNonEmptyString(args.existingDialog?.parentDialogId) ??
|
|
51
|
+
parentDialogId;
|
|
52
|
+
return {
|
|
53
|
+
...(inheritedFromDialogKey ? { inheritedFromDialogKey } : {}),
|
|
54
|
+
...(parentDialogId ? { parentDialogId } : {}),
|
|
55
|
+
...(rootDialogId ? { rootDialogId } : {}),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
37
59
|
function buildDialogMessageWriteOps(args: {
|
|
38
60
|
dialogId: string;
|
|
39
61
|
input: AgentRuntimeSaveTurnInput;
|
|
@@ -102,6 +124,12 @@ export function buildAgentRuntimeDialogWritePlan(args: {
|
|
|
102
124
|
updatedAt: nowIso,
|
|
103
125
|
finishedAt: args.now,
|
|
104
126
|
usage: args.input.result.usage,
|
|
127
|
+
...(normalizeNonEmptyString(args.input.spaceId) ? { spaceId: normalizeNonEmptyString(args.input.spaceId) } : {}),
|
|
128
|
+
...(normalizeNonEmptyString(args.input.category) ? { category: normalizeNonEmptyString(args.input.category) } : {}),
|
|
129
|
+
...buildDialogLineageFields({
|
|
130
|
+
input: args.input,
|
|
131
|
+
existingDialog: args.existingDialog,
|
|
132
|
+
}),
|
|
105
133
|
...(typeof args.input.result.toolCallCount === "number"
|
|
106
134
|
? { toolCallCount: args.input.result.toolCallCount }
|
|
107
135
|
: {}),
|
|
@@ -109,6 +137,17 @@ export function buildAgentRuntimeDialogWritePlan(args: {
|
|
|
109
137
|
host: args.runtimeHost,
|
|
110
138
|
...(args.runtimeMetadata ?? {}),
|
|
111
139
|
},
|
|
140
|
+
...(args.input.result.runtimeToolSurface
|
|
141
|
+
? {
|
|
142
|
+
runtimeCheckpoint: {
|
|
143
|
+
...(args.existingDialog?.runtimeCheckpoint &&
|
|
144
|
+
typeof args.existingDialog.runtimeCheckpoint === "object"
|
|
145
|
+
? args.existingDialog.runtimeCheckpoint
|
|
146
|
+
: {}),
|
|
147
|
+
toolSurface: args.input.result.runtimeToolSurface,
|
|
148
|
+
},
|
|
149
|
+
}
|
|
150
|
+
: {}),
|
|
112
151
|
};
|
|
113
152
|
return {
|
|
114
153
|
dialogId,
|
|
@@ -27,7 +27,6 @@ export type AgentRuntimeAgentConfig = {
|
|
|
27
27
|
reasoning_effort?: string;
|
|
28
28
|
runtimeBinding?: Record<string, unknown>;
|
|
29
29
|
runtimeToolPolicy?: AgentRuntimeToolPolicy;
|
|
30
|
-
localWorkspaceMode?: "current" | "task-worktree";
|
|
31
30
|
delegation?: Record<string, unknown>;
|
|
32
31
|
rawRecord?: Record<string, unknown>;
|
|
33
32
|
};
|
|
@@ -57,6 +56,10 @@ export type AgentRuntimeSaveTurnInput = {
|
|
|
57
56
|
messages: AgentRuntimeChatMessage[];
|
|
58
57
|
result: AgentRuntimeResult;
|
|
59
58
|
continueDialogId?: string;
|
|
59
|
+
spaceId?: string;
|
|
60
|
+
category?: string;
|
|
61
|
+
inheritedFromDialogKey?: string;
|
|
62
|
+
parentDialogId?: string;
|
|
60
63
|
};
|
|
61
64
|
|
|
62
65
|
export type AgentRuntimeHostAdapter = {
|
|
@@ -126,7 +126,7 @@ export function createHybridRecordStore(
|
|
|
126
126
|
return {
|
|
127
127
|
read: async (dbKey, options) => {
|
|
128
128
|
const localRecord = await readHybridLocalRecord(deps.db, dbKey);
|
|
129
|
-
if (localRecord) return
|
|
129
|
+
if (localRecord) return normalizeHybridRecord(dbKey, localRecord);
|
|
130
130
|
if (options?.remote === false) return null;
|
|
131
131
|
|
|
132
132
|
for (const server of resolveHybridServers(
|
package/agent-runtime/index.ts
CHANGED
|
@@ -39,15 +39,6 @@ export {
|
|
|
39
39
|
createHybridRecordStore,
|
|
40
40
|
shouldCacheHybridRemoteRecord,
|
|
41
41
|
} from "./hybridRecordStore";
|
|
42
|
-
export {
|
|
43
|
-
activateWorkspaceSession,
|
|
44
|
-
createWorkspaceSession,
|
|
45
|
-
formatWorkspaceSessionActivation,
|
|
46
|
-
} from "./workspaceSession";
|
|
47
|
-
export {
|
|
48
|
-
formatPreparedGitTaskWorkspace,
|
|
49
|
-
prepareGitTaskWorkspace,
|
|
50
|
-
} from "./taskWorkspace";
|
|
51
42
|
export {
|
|
52
43
|
executeLocalToolWithPolicy,
|
|
53
44
|
resolveLocalToolPolicy,
|
|
@@ -65,12 +56,44 @@ export {
|
|
|
65
56
|
resolveLocalRuntimeEnvFromPolicy,
|
|
66
57
|
resolveLocalWorkspaceExecutorOptionsFromPolicy,
|
|
67
58
|
} from "./runtimeToolPolicy";
|
|
59
|
+
export {
|
|
60
|
+
DEFAULT_PRIVATE_NOLO_WORKSPACE_TOOLS,
|
|
61
|
+
inferOwnerIdFromRuntimeAgentKey,
|
|
62
|
+
isPublicRuntimeAgentRef,
|
|
63
|
+
redactAgentRecordForWorkspaceTool,
|
|
64
|
+
resolveRuntimeToolSurfaceForAgent,
|
|
65
|
+
resolveRuntimeToolSurface,
|
|
66
|
+
} from "./runtimeToolSurface";
|
|
68
67
|
export {
|
|
69
68
|
buildLocalWorkspaceOpenAiTools,
|
|
70
69
|
buildLocalWorkspacePolicyToolNames,
|
|
71
70
|
buildLocalWorkspaceToolset,
|
|
72
71
|
createLocalWorkspaceToolExecutors,
|
|
73
72
|
} from "./localWorkspaceTools";
|
|
73
|
+
export {
|
|
74
|
+
buildNoloWorkspaceCommandArgs,
|
|
75
|
+
buildNoloWorkspaceCliToolExecutors,
|
|
76
|
+
buildNoloWorkspaceOpenAiTools,
|
|
77
|
+
buildNoloTableQueryRequest,
|
|
78
|
+
clampNoloPositiveInteger,
|
|
79
|
+
filterNoloWorkspaceToolNames,
|
|
80
|
+
getNoloComparableUpdatedAt,
|
|
81
|
+
getNoloDialogIdFromKey,
|
|
82
|
+
getNoloSpaceContentKeys,
|
|
83
|
+
isNoloWorkspaceToolName,
|
|
84
|
+
noloPositiveIntegerString,
|
|
85
|
+
noloStringArg,
|
|
86
|
+
normalizeNoloDocReadArgs,
|
|
87
|
+
normalizeNoloSpaceInput,
|
|
88
|
+
NOLO_WORKSPACE_TOOL_NAMES,
|
|
89
|
+
NOLO_WORKSPACE_TOOL_PROMPT,
|
|
90
|
+
parseNoloWorkspaceToolArguments,
|
|
91
|
+
resolveNoloDialogInput,
|
|
92
|
+
runNoloWorkspaceCliTool,
|
|
93
|
+
} from "./noloWorkspaceTools";
|
|
94
|
+
export type {
|
|
95
|
+
NoloWorkspaceToolName,
|
|
96
|
+
} from "./noloWorkspaceTools";
|
|
74
97
|
export type {
|
|
75
98
|
AgentRuntimeAgentConfig,
|
|
76
99
|
AgentRuntimeHostAdapter,
|
|
@@ -87,15 +110,6 @@ export type {
|
|
|
87
110
|
HybridRecordKvDb,
|
|
88
111
|
HybridRecordStore,
|
|
89
112
|
} from "./hybridRecordStore";
|
|
90
|
-
export type {
|
|
91
|
-
PreparedTaskWorkspace,
|
|
92
|
-
PrepareTaskWorkspace,
|
|
93
|
-
WorkspaceSession,
|
|
94
|
-
WorkspaceSessionMode,
|
|
95
|
-
} from "./workspaceSession";
|
|
96
|
-
export type {
|
|
97
|
-
PreparedGitTaskWorkspace,
|
|
98
|
-
} from "./taskWorkspace";
|
|
99
113
|
export type {
|
|
100
114
|
LocalToolPolicyDecision,
|
|
101
115
|
} from "./localToolPolicy";
|
|
@@ -105,6 +119,13 @@ export type {
|
|
|
105
119
|
export type {
|
|
106
120
|
PlatformChatProviderConfig,
|
|
107
121
|
} from "./platformChatProvider";
|
|
122
|
+
export type {
|
|
123
|
+
RuntimeToolSurfaceHost,
|
|
124
|
+
RuntimeToolSurfaceForAgentInput,
|
|
125
|
+
RuntimeToolSurfaceInput,
|
|
126
|
+
RuntimeToolSurfaceResult,
|
|
127
|
+
RuntimeToolSurfaceVisibility,
|
|
128
|
+
} from "./runtimeToolSurface";
|
|
108
129
|
export type {
|
|
109
130
|
AgentRuntimeChatMessage,
|
|
110
131
|
AgentRuntimeDecision,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type LocalDialogReadResult = {
|
|
2
|
+
meta: any;
|
|
3
|
+
msgs: any[];
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export async function readDialogFromLocalDb(args: {
|
|
7
|
+
dialogKey: string;
|
|
8
|
+
dialogId: string;
|
|
9
|
+
limit: number;
|
|
10
|
+
}): Promise<LocalDialogReadResult> {
|
|
11
|
+
const [{ default: serverDb, ensureServerDbOpen }, { fetchMessages }] = await Promise.all([
|
|
12
|
+
import("../database/server/db"),
|
|
13
|
+
import("../chat/messages/fetchMessages"),
|
|
14
|
+
]);
|
|
15
|
+
await ensureServerDbOpen();
|
|
16
|
+
return {
|
|
17
|
+
meta: await serverDb.get(args.dialogKey),
|
|
18
|
+
msgs: await fetchMessages(serverDb, args.dialogId, {
|
|
19
|
+
limit: args.limit,
|
|
20
|
+
throwOnError: true,
|
|
21
|
+
}),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -12,7 +12,10 @@ export type LocalAgentTurnInput = {
|
|
|
12
12
|
agentRef: string;
|
|
13
13
|
input: AgentRuntimeMessageContent;
|
|
14
14
|
continueDialogId?: string;
|
|
15
|
-
|
|
15
|
+
spaceId?: string;
|
|
16
|
+
category?: string;
|
|
17
|
+
inheritedFromDialogKey?: string;
|
|
18
|
+
parentDialogId?: string;
|
|
16
19
|
timeoutMs?: number;
|
|
17
20
|
onToolEvent?: (event: LocalAgentToolEvent) => void;
|
|
18
21
|
};
|
|
@@ -26,11 +29,13 @@ export type LocalAgentToolEvent = {
|
|
|
26
29
|
round: number;
|
|
27
30
|
toolCallId: string;
|
|
28
31
|
toolName: string;
|
|
32
|
+
argumentsPreview?: string;
|
|
33
|
+
elapsedMs?: number;
|
|
34
|
+
summary?: string;
|
|
29
35
|
message?: string;
|
|
30
36
|
metadata?: Record<string, unknown>;
|
|
31
37
|
};
|
|
32
38
|
|
|
33
|
-
export const DEFAULT_LOCAL_AGENT_MAX_TOOL_ROUNDS = 128;
|
|
34
39
|
export const LOCAL_AGENT_CONFIG_MISSING_CODE = "LOCAL_AGENT_CONFIG_MISSING";
|
|
35
40
|
|
|
36
41
|
function formatToolExecutionError(args: {
|
|
@@ -75,6 +80,72 @@ function emitToolEvent(
|
|
|
75
80
|
input.onToolEvent?.(event);
|
|
76
81
|
}
|
|
77
82
|
|
|
83
|
+
function compactWhitespace(value: string) {
|
|
84
|
+
return value.replace(/\s+/g, " ").trim();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function clip(value: string, max = 240) {
|
|
88
|
+
const compact = compactWhitespace(value);
|
|
89
|
+
return compact.length > max ? `${compact.slice(0, max - 3)}...` : compact;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function parseToolArguments(raw: string | undefined) {
|
|
93
|
+
if (!raw?.trim()) return {};
|
|
94
|
+
try {
|
|
95
|
+
const parsed = JSON.parse(raw);
|
|
96
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
97
|
+
? parsed as Record<string, unknown>
|
|
98
|
+
: {};
|
|
99
|
+
} catch {
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function summarizeToolArguments(toolName: string, rawArgs: string | undefined) {
|
|
105
|
+
const args = parseToolArguments(rawArgs);
|
|
106
|
+
const pick = (...keys: string[]) => {
|
|
107
|
+
for (const key of keys) {
|
|
108
|
+
const value = args[key];
|
|
109
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
110
|
+
}
|
|
111
|
+
return "";
|
|
112
|
+
};
|
|
113
|
+
const command = pick("command", "cmd", "runCommand", "executeCommand", "bash");
|
|
114
|
+
if (command) return clip(command);
|
|
115
|
+
const filePath = pick("filePath", "path", "filename", "file");
|
|
116
|
+
if (filePath) return clip(filePath);
|
|
117
|
+
const query = pick("query", "pattern", "search", "q");
|
|
118
|
+
if (query) return clip(query);
|
|
119
|
+
const keys = Object.keys(args);
|
|
120
|
+
if (keys.length === 0) return "";
|
|
121
|
+
return clip(keys.slice(0, 6).map((key) => {
|
|
122
|
+
const value = args[key];
|
|
123
|
+
if (typeof value === "string") return `${key}=${clip(value, 80)}`;
|
|
124
|
+
if (typeof value === "number" || typeof value === "boolean") return `${key}=${String(value)}`;
|
|
125
|
+
if (Array.isArray(value)) return `${key}[${value.length}]`;
|
|
126
|
+
return `${key}=${value === null ? "null" : typeof value}`;
|
|
127
|
+
}).join(" "));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function summarizeToolResult(content: unknown, metadata?: Record<string, unknown>) {
|
|
131
|
+
const parts: string[] = [];
|
|
132
|
+
const exitCode = metadata?.exitCode;
|
|
133
|
+
if (typeof exitCode === "number") parts.push(`exit=${exitCode}`);
|
|
134
|
+
if (typeof content === "string") {
|
|
135
|
+
const trimmed = content.trim();
|
|
136
|
+
if (trimmed) {
|
|
137
|
+
const lines = trimmed.split(/\r?\n/).length;
|
|
138
|
+
parts.push(`${lines} line${lines === 1 ? "" : "s"}`);
|
|
139
|
+
parts.push(`${trimmed.length} chars`);
|
|
140
|
+
const tail = clip(trimmed.slice(-160), 160);
|
|
141
|
+
if (tail) parts.push(`tail="${tail}"`);
|
|
142
|
+
} else {
|
|
143
|
+
parts.push("empty");
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return parts.join(" ");
|
|
147
|
+
}
|
|
148
|
+
|
|
78
149
|
function buildMessages(args: {
|
|
79
150
|
prompt?: string;
|
|
80
151
|
history: AgentRuntimeChatMessage[];
|
|
@@ -124,19 +195,16 @@ export async function runLocalAgentTurn(
|
|
|
124
195
|
input: input.input,
|
|
125
196
|
});
|
|
126
197
|
const provider = await input.adapter.resolveProvider(agentConfig);
|
|
127
|
-
const maxToolRounds = input.maxToolRounds ?? DEFAULT_LOCAL_AGENT_MAX_TOOL_ROUNDS;
|
|
128
198
|
const userInputText = extractUserInputText(input.input);
|
|
129
199
|
let toolCallCount = 0;
|
|
130
200
|
let result: AgentRuntimeResult;
|
|
131
|
-
|
|
201
|
+
let round = 0;
|
|
202
|
+
while (true) {
|
|
132
203
|
result = await provider.complete(messages, {
|
|
133
204
|
...(typeof input.timeoutMs === "number" ? { timeoutMs: input.timeoutMs } : {}),
|
|
134
205
|
});
|
|
135
206
|
const toolCalls = result.tool_calls ?? [];
|
|
136
207
|
if (toolCalls.length === 0) break;
|
|
137
|
-
if (round === maxToolRounds) {
|
|
138
|
-
throw new Error(`Local agent exceeded max tool rounds: ${maxToolRounds}`);
|
|
139
|
-
}
|
|
140
208
|
toolCallCount += toolCalls.length;
|
|
141
209
|
messages.push({
|
|
142
210
|
role: "assistant",
|
|
@@ -147,11 +215,13 @@ export async function runLocalAgentTurn(
|
|
|
147
215
|
for (const toolCall of toolCalls) {
|
|
148
216
|
const toolName = toolCall.function.name;
|
|
149
217
|
let toolResult;
|
|
218
|
+
const startedAt = Date.now();
|
|
150
219
|
emitToolEvent(input, {
|
|
151
220
|
type: "tool-call",
|
|
152
221
|
round,
|
|
153
222
|
toolCallId: toolCall.id,
|
|
154
223
|
toolName,
|
|
224
|
+
argumentsPreview: summarizeToolArguments(toolName, toolCall.function.arguments),
|
|
155
225
|
});
|
|
156
226
|
try {
|
|
157
227
|
toolResult = await input.adapter.executeTool({
|
|
@@ -165,6 +235,8 @@ export async function runLocalAgentTurn(
|
|
|
165
235
|
round,
|
|
166
236
|
toolCallId: toolCall.id,
|
|
167
237
|
toolName,
|
|
238
|
+
elapsedMs: Math.max(0, Date.now() - startedAt),
|
|
239
|
+
summary: summarizeToolResult(toolResult.content, toolResult.metadata),
|
|
168
240
|
metadata: toolResult.metadata,
|
|
169
241
|
});
|
|
170
242
|
} catch (error) {
|
|
@@ -174,6 +246,7 @@ export async function runLocalAgentTurn(
|
|
|
174
246
|
round,
|
|
175
247
|
toolCallId: toolCall.id,
|
|
176
248
|
toolName,
|
|
249
|
+
elapsedMs: Math.max(0, Date.now() - startedAt),
|
|
177
250
|
message: error instanceof Error ? error.message : String(error),
|
|
178
251
|
});
|
|
179
252
|
toolResult = {
|
|
@@ -201,6 +274,7 @@ export async function runLocalAgentTurn(
|
|
|
201
274
|
...(toolResult.metadata ? { tool_result_metadata: toolResult.metadata } : {}),
|
|
202
275
|
});
|
|
203
276
|
}
|
|
277
|
+
round += 1;
|
|
204
278
|
}
|
|
205
279
|
result = result!;
|
|
206
280
|
messages.push({
|
|
@@ -213,13 +287,19 @@ export async function runLocalAgentTurn(
|
|
|
213
287
|
result: {
|
|
214
288
|
...result,
|
|
215
289
|
...(toolCallCount > 0 ? { toolCallCount } : {}),
|
|
290
|
+
...((agentConfig as any).toolSurface ? { runtimeToolSurface: (agentConfig as any).toolSurface } : {}),
|
|
216
291
|
},
|
|
217
292
|
...(input.continueDialogId ? { continueDialogId: input.continueDialogId } : {}),
|
|
293
|
+
...(input.spaceId ? { spaceId: input.spaceId } : {}),
|
|
294
|
+
...(input.category ? { category: input.category } : {}),
|
|
295
|
+
...(input.inheritedFromDialogKey ? { inheritedFromDialogKey: input.inheritedFromDialogKey } : {}),
|
|
296
|
+
...(input.parentDialogId ? { parentDialogId: input.parentDialogId } : {}),
|
|
218
297
|
});
|
|
219
298
|
|
|
220
299
|
return {
|
|
221
300
|
...result,
|
|
222
301
|
...(toolCallCount > 0 ? { toolCallCount } : {}),
|
|
302
|
+
...((agentConfig as any).toolSurface ? { runtimeToolSurface: (agentConfig as any).toolSurface } : {}),
|
|
223
303
|
dialogId: saved.dialogId,
|
|
224
304
|
};
|
|
225
305
|
}
|
|
@@ -28,16 +28,11 @@ const REMOVED_LOCAL_TOOLS = new Set([
|
|
|
28
28
|
]);
|
|
29
29
|
|
|
30
30
|
const DEFAULT_LOCAL_TOOLS = new Set([
|
|
31
|
-
"listWorkspaceFiles",
|
|
32
|
-
"readWorkspaceFile",
|
|
33
|
-
"writeWorkspaceFile",
|
|
34
|
-
"replaceWorkspaceText",
|
|
35
31
|
"listFiles",
|
|
36
32
|
"readFile",
|
|
37
33
|
"writeFile",
|
|
38
34
|
"editFile",
|
|
39
35
|
"searchFiles",
|
|
40
|
-
"searchWorkspace",
|
|
41
36
|
"execShell",
|
|
42
37
|
]);
|
|
43
38
|
|
|
@@ -66,10 +61,6 @@ function parseShellCommandPayload(rawArguments: string) {
|
|
|
66
61
|
}
|
|
67
62
|
}
|
|
68
63
|
|
|
69
|
-
function isLocalShellModeEnabled(env: EnvLike) {
|
|
70
|
-
return ["worktree", "dangerous", "1", "true"].includes(env.NOLO_LOCAL_SHELL_MODE || "");
|
|
71
|
-
}
|
|
72
|
-
|
|
73
64
|
function isRestrictedLocalToolMode(env: EnvLike) {
|
|
74
65
|
return env.NOLO_LOCAL_TOOL_MODE === "restricted";
|
|
75
66
|
}
|
|
@@ -101,14 +92,7 @@ export function resolveLocalToolPolicy(args: {
|
|
|
101
92
|
}
|
|
102
93
|
|
|
103
94
|
if (toolName === "execShell") {
|
|
104
|
-
|
|
105
|
-
return { allowed: true, toolName };
|
|
106
|
-
}
|
|
107
|
-
return {
|
|
108
|
-
allowed: false,
|
|
109
|
-
toolName,
|
|
110
|
-
reason: "execShell requires NOLO_LOCAL_SHELL_MODE=worktree.",
|
|
111
|
-
};
|
|
95
|
+
return { allowed: true, toolName };
|
|
112
96
|
}
|
|
113
97
|
|
|
114
98
|
if (NEVER_LOCAL_TOOLS.has(toolName)) {
|