nolo-cli 0.1.38 → 0.1.39
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/agent-runtime/agentRecordConfig.ts +0 -14
- package/agent-runtime/hostAdapter.ts +0 -1
- package/agent-runtime/index.ts +24 -18
- package/agent-runtime/localDialogRead.ts +23 -0
- package/agent-runtime/localToolPolicy.ts +2 -7
- package/agent-runtime/localWorkspaceTools.ts +9 -0
- package/agent-runtime/noloWorkspaceTools.ts +371 -0
- package/agent-runtime/runtimeToolPolicy.ts +1 -3
- package/agent-runtime/types.ts +1 -1
- package/agentAliases.ts +6 -0
- package/agentRunCommand.ts +57 -65
- package/agentRuntimeLocal.ts +0 -7
- package/chat/messages/fetchMessages.ts +84 -0
- package/chat/messages/types.ts +185 -0
- package/cli/agentAliases.ts +6 -0
- package/cli/agentRunCommand.ts +57 -65
- package/cli/agentRuntimeLocal.ts +0 -7
- package/cli/cliEnvHelpers.ts +11 -7
- package/cli/client/agentRun.ts +55 -7
- package/cli/client/localRuntimeAdapter.ts +87 -59
- package/cli/connectorRunArtifact.ts +0 -1
- package/cli/dialogCommands.ts +572 -0
- package/cli/dialogInternalCommandEntries.ts +4 -0
- package/cli/machineWsRunDispatch.ts +29 -37
- 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 +2 -3
- package/client/agentRun.test.ts +185 -1
- package/client/agentRun.ts +55 -7
- package/client/localRuntimeAdapter.test.ts +90 -25
- package/client/localRuntimeAdapter.ts +87 -59
- package/connectorRunArtifact.ts +0 -1
- package/database/server/db.ts +99 -0
- package/database/server/ensureDbOpen.ts +12 -0
- 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 +29 -37
- package/package.json +10 -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
|
@@ -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
|
};
|
|
@@ -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
|
};
|
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,
|
|
@@ -71,6 +62,30 @@ export {
|
|
|
71
62
|
buildLocalWorkspaceToolset,
|
|
72
63
|
createLocalWorkspaceToolExecutors,
|
|
73
64
|
} from "./localWorkspaceTools";
|
|
65
|
+
export {
|
|
66
|
+
buildNoloWorkspaceCommandArgs,
|
|
67
|
+
buildNoloWorkspaceCliToolExecutors,
|
|
68
|
+
buildNoloWorkspaceOpenAiTools,
|
|
69
|
+
buildNoloTableQueryRequest,
|
|
70
|
+
clampNoloPositiveInteger,
|
|
71
|
+
filterNoloWorkspaceToolNames,
|
|
72
|
+
getNoloComparableUpdatedAt,
|
|
73
|
+
getNoloDialogIdFromKey,
|
|
74
|
+
getNoloSpaceContentKeys,
|
|
75
|
+
isNoloWorkspaceToolName,
|
|
76
|
+
noloPositiveIntegerString,
|
|
77
|
+
noloStringArg,
|
|
78
|
+
normalizeNoloDocReadArgs,
|
|
79
|
+
normalizeNoloSpaceInput,
|
|
80
|
+
NOLO_WORKSPACE_TOOL_NAMES,
|
|
81
|
+
NOLO_WORKSPACE_TOOL_PROMPT,
|
|
82
|
+
parseNoloWorkspaceToolArguments,
|
|
83
|
+
resolveNoloDialogInput,
|
|
84
|
+
runNoloWorkspaceCliTool,
|
|
85
|
+
} from "./noloWorkspaceTools";
|
|
86
|
+
export type {
|
|
87
|
+
NoloWorkspaceToolName,
|
|
88
|
+
} from "./noloWorkspaceTools";
|
|
74
89
|
export type {
|
|
75
90
|
AgentRuntimeAgentConfig,
|
|
76
91
|
AgentRuntimeHostAdapter,
|
|
@@ -87,15 +102,6 @@ export type {
|
|
|
87
102
|
HybridRecordKvDb,
|
|
88
103
|
HybridRecordStore,
|
|
89
104
|
} 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
105
|
export type {
|
|
100
106
|
LocalToolPolicyDecision,
|
|
101
107
|
} from "./localToolPolicy";
|
|
@@ -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
|
+
}
|
|
@@ -38,7 +38,6 @@ const DEFAULT_LOCAL_TOOLS = new Set([
|
|
|
38
38
|
"editFile",
|
|
39
39
|
"searchFiles",
|
|
40
40
|
"searchWorkspace",
|
|
41
|
-
"execShell",
|
|
42
41
|
]);
|
|
43
42
|
|
|
44
43
|
function parseToolAllowlist(value: string | undefined) {
|
|
@@ -66,10 +65,6 @@ function parseShellCommandPayload(rawArguments: string) {
|
|
|
66
65
|
}
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
function isLocalShellModeEnabled(env: EnvLike) {
|
|
70
|
-
return ["worktree", "dangerous", "1", "true"].includes(env.NOLO_LOCAL_SHELL_MODE || "");
|
|
71
|
-
}
|
|
72
|
-
|
|
73
68
|
function isRestrictedLocalToolMode(env: EnvLike) {
|
|
74
69
|
return env.NOLO_LOCAL_TOOL_MODE === "restricted";
|
|
75
70
|
}
|
|
@@ -101,13 +96,13 @@ export function resolveLocalToolPolicy(args: {
|
|
|
101
96
|
}
|
|
102
97
|
|
|
103
98
|
if (toolName === "execShell") {
|
|
104
|
-
if (
|
|
99
|
+
if (args.env.NOLO_LOCAL_SHELL_MODE === "worktree") {
|
|
105
100
|
return { allowed: true, toolName };
|
|
106
101
|
}
|
|
107
102
|
return {
|
|
108
103
|
allowed: false,
|
|
109
104
|
toolName,
|
|
110
|
-
reason: "execShell requires NOLO_LOCAL_SHELL_MODE=worktree.",
|
|
105
|
+
reason: "execShell requires NOLO_LOCAL_SHELL_MODE=worktree for local runtime runs.",
|
|
111
106
|
};
|
|
112
107
|
}
|
|
113
108
|
|
|
@@ -20,6 +20,7 @@ type WorkspaceFileArgs = {
|
|
|
20
20
|
newText?: unknown;
|
|
21
21
|
expectedReplacements?: unknown;
|
|
22
22
|
query?: unknown;
|
|
23
|
+
includeIgnored?: unknown;
|
|
23
24
|
command?: unknown;
|
|
24
25
|
cmd?: unknown;
|
|
25
26
|
branch?: unknown;
|
|
@@ -188,6 +189,11 @@ function buildSearchWorkspaceTool(): OpenAiCompatibleTool {
|
|
|
188
189
|
description: "Search query or regular expression.",
|
|
189
190
|
},
|
|
190
191
|
path: buildWorkspacePathProperty(),
|
|
192
|
+
includeIgnored: {
|
|
193
|
+
type: "boolean",
|
|
194
|
+
description:
|
|
195
|
+
"When true, search files ignored by .gitignore such as .tmp. Defaults to false; .git and node_modules remain excluded.",
|
|
196
|
+
},
|
|
191
197
|
},
|
|
192
198
|
required: ["query"],
|
|
193
199
|
},
|
|
@@ -913,6 +919,7 @@ async function searchWorkspaceTool(args: {
|
|
|
913
919
|
const requestedPath = typeof parsed.path === "string" && parsed.path.trim()
|
|
914
920
|
? parsed.path.trim()
|
|
915
921
|
: ".";
|
|
922
|
+
const includeIgnored = parsed.includeIgnored === true;
|
|
916
923
|
const searchPath = resolveLocalWorkspaceToolPath({
|
|
917
924
|
workspaceRoot: args.workspaceRoot,
|
|
918
925
|
requestedPath,
|
|
@@ -930,6 +937,7 @@ async function searchWorkspaceTool(args: {
|
|
|
930
937
|
"--line-number",
|
|
931
938
|
"--no-heading",
|
|
932
939
|
"--hidden",
|
|
940
|
+
...(includeIgnored ? ["--no-ignore"] : []),
|
|
933
941
|
"--glob",
|
|
934
942
|
"!node_modules",
|
|
935
943
|
"--glob",
|
|
@@ -959,6 +967,7 @@ async function searchWorkspaceTool(args: {
|
|
|
959
967
|
metadata: {
|
|
960
968
|
query,
|
|
961
969
|
path: requestedPath,
|
|
970
|
+
includeIgnored,
|
|
962
971
|
exitCode: result.exitCode,
|
|
963
972
|
},
|
|
964
973
|
};
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
export const NOLO_WORKSPACE_TOOL_NAMES = [
|
|
2
|
+
"listDialogs",
|
|
3
|
+
"readDialog",
|
|
4
|
+
"listAgents",
|
|
5
|
+
"readAgent",
|
|
6
|
+
"listSpaces",
|
|
7
|
+
"readSpace",
|
|
8
|
+
"readDoc",
|
|
9
|
+
"readSkillDoc",
|
|
10
|
+
"queryTableRows",
|
|
11
|
+
"cliWhoami",
|
|
12
|
+
"cliDoctor",
|
|
13
|
+
] as const;
|
|
14
|
+
|
|
15
|
+
export type NoloWorkspaceToolName = typeof NOLO_WORKSPACE_TOOL_NAMES[number];
|
|
16
|
+
|
|
17
|
+
export const NOLO_WORKSPACE_TOOL_PROMPT =
|
|
18
|
+
"Nolo workspace tools are available for Nolo data: use listDialogs/readDialog, listAgents/readAgent, listSpaces/readSpace, readDoc/readSkillDoc, queryTableRows, cliWhoami, and cliDoctor when the user asks to inspect Nolo workspace data. Prefer tools over guessing, and combine tool results when the user asks for summaries or analysis.";
|
|
19
|
+
|
|
20
|
+
const NOLO_WORKSPACE_TOOL_NAME_SET = new Set<string>(NOLO_WORKSPACE_TOOL_NAMES);
|
|
21
|
+
const DIALOG_ID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/i;
|
|
22
|
+
const DIALOG_PATH_RE = /^\/(?:space\/([^/]+)\/)?dialog-(.+)-([0-9A-HJKMNP-TV-Z]{26})\/?$/i;
|
|
23
|
+
|
|
24
|
+
function stringToolParam(description: string) {
|
|
25
|
+
return { type: "string", description };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function isNoloWorkspaceToolName(toolName: unknown): toolName is NoloWorkspaceToolName {
|
|
29
|
+
return typeof toolName === "string" && NOLO_WORKSPACE_TOOL_NAME_SET.has(toolName);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function filterNoloWorkspaceToolNames(toolNames?: string[]) {
|
|
33
|
+
return (toolNames ?? []).filter(isNoloWorkspaceToolName);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function buildNoloWorkspaceOpenAiTools(args: { toolNames?: string[] }) {
|
|
37
|
+
const toolNames = new Set(args.toolNames ?? []);
|
|
38
|
+
const tools: Array<Record<string, unknown>> = [];
|
|
39
|
+
function add(name: NoloWorkspaceToolName, description: string, properties: Record<string, unknown>, required: string[] = []) {
|
|
40
|
+
if (!toolNames.has(name)) return;
|
|
41
|
+
tools.push({
|
|
42
|
+
type: "function",
|
|
43
|
+
function: {
|
|
44
|
+
name,
|
|
45
|
+
description,
|
|
46
|
+
parameters: {
|
|
47
|
+
type: "object",
|
|
48
|
+
properties,
|
|
49
|
+
...(required.length ? { required } : {}),
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
add("listDialogs", "List the current user's dialogs in the Nolo workspace.", {
|
|
56
|
+
limit: { type: "integer", description: "Maximum dialogs to return." },
|
|
57
|
+
space: stringToolParam("Optional space id or URL."),
|
|
58
|
+
});
|
|
59
|
+
add("readDialog", "Read one persisted dialog in the Nolo workspace.", {
|
|
60
|
+
dialog: stringToolParam("Dialog id, dialog db key, or dialog URL."),
|
|
61
|
+
limit: { type: "integer", description: "Optional message limit." },
|
|
62
|
+
}, ["dialog"]);
|
|
63
|
+
add("listAgents", "List the current user's agents in the Nolo workspace.", {
|
|
64
|
+
space: stringToolParam("Optional space id or URL."),
|
|
65
|
+
publicOnly: { type: "boolean", description: "Only show public agents." },
|
|
66
|
+
});
|
|
67
|
+
add("readAgent", "Read one agent config in the Nolo workspace.", {
|
|
68
|
+
agent: stringToolParam("Agent key, id, alias, or URL."),
|
|
69
|
+
}, ["agent"]);
|
|
70
|
+
add("listSpaces", "List joined spaces in the Nolo workspace.", {});
|
|
71
|
+
add("readSpace", "Read one space in the Nolo workspace.", {
|
|
72
|
+
space: stringToolParam("Space id or URL."),
|
|
73
|
+
contentKey: stringToolParam("Optional content key inside the space."),
|
|
74
|
+
brief: { type: "boolean", description: "Return brief output when supported." },
|
|
75
|
+
}, ["space"]);
|
|
76
|
+
add("readDoc", "Read one normal doc/page in the Nolo workspace.", {
|
|
77
|
+
doc: stringToolParam("Doc/page key."),
|
|
78
|
+
}, ["doc"]);
|
|
79
|
+
add("readSkillDoc", "Read one skill doc in the Nolo workspace.", {
|
|
80
|
+
doc: stringToolParam("Skill doc/page key."),
|
|
81
|
+
}, ["doc"]);
|
|
82
|
+
add("queryTableRows", "Query rows from a Nolo table.", {
|
|
83
|
+
table: stringToolParam("Table id or meta key."),
|
|
84
|
+
limit: { type: "integer", description: "Optional row limit." },
|
|
85
|
+
row: stringToolParam("Optional row id or row db key."),
|
|
86
|
+
output: stringToolParam("Optional output format such as json, jsonl, or items."),
|
|
87
|
+
}, ["table"]);
|
|
88
|
+
add("cliWhoami", "Show the current Nolo CLI login state.", {});
|
|
89
|
+
add("cliDoctor", "Show Nolo CLI doctor diagnostics.", {});
|
|
90
|
+
return tools;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function noloPositiveIntegerString(value: unknown) {
|
|
94
|
+
const parsed = Number(value);
|
|
95
|
+
return Number.isInteger(parsed) && parsed > 0 ? String(parsed) : null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function noloStringArg(value: unknown) {
|
|
99
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function parseNoloWorkspaceToolArguments(raw: string) {
|
|
103
|
+
try {
|
|
104
|
+
const parsed = JSON.parse(raw || "{}");
|
|
105
|
+
return parsed && typeof parsed === "object" ? parsed as Record<string, unknown> : {};
|
|
106
|
+
} catch {
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function clampNoloPositiveInteger(value: unknown, fallback: number, max = 500) {
|
|
112
|
+
const parsed = Number(value);
|
|
113
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
|
|
114
|
+
return Math.min(Math.floor(parsed), max);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function getNoloDialogIdFromKey(dbKey: string) {
|
|
118
|
+
const index = dbKey.lastIndexOf("-");
|
|
119
|
+
return index >= 0 ? dbKey.slice(index + 1) : dbKey;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function resolveNoloDialogInput(rawInput: string, userId: string) {
|
|
123
|
+
const raw = rawInput.trim();
|
|
124
|
+
if (raw.startsWith("http://") || raw.startsWith("https://")) {
|
|
125
|
+
const url = new URL(raw);
|
|
126
|
+
const match = url.pathname.match(DIALOG_PATH_RE);
|
|
127
|
+
if (!match) throw new Error(`Unsupported dialog URL path: ${url.pathname}`);
|
|
128
|
+
const [, spaceId, ownerId, dialogId] = match;
|
|
129
|
+
return {
|
|
130
|
+
dbKey: `dialog-${ownerId}-${dialogId}`,
|
|
131
|
+
dialogId,
|
|
132
|
+
ownerId,
|
|
133
|
+
spaceId: spaceId ? decodeURIComponent(spaceId) : null,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (raw.startsWith("dialog-")) {
|
|
137
|
+
const dialogId = getNoloDialogIdFromKey(raw);
|
|
138
|
+
return {
|
|
139
|
+
dbKey: raw,
|
|
140
|
+
dialogId,
|
|
141
|
+
ownerId: raw.slice("dialog-".length, Math.max("dialog-".length, raw.length - dialogId.length - 1)),
|
|
142
|
+
spaceId: null,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
if (!DIALOG_ID_RE.test(raw)) {
|
|
146
|
+
throw new Error(`Unsupported dialog id: ${raw}`);
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
dbKey: `dialog-${userId}-${raw}`,
|
|
150
|
+
dialogId: raw,
|
|
151
|
+
ownerId: userId,
|
|
152
|
+
spaceId: null,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function normalizeNoloSpaceInput(raw: string) {
|
|
157
|
+
const value = raw.trim();
|
|
158
|
+
if (!value) return "";
|
|
159
|
+
if (value.startsWith("http://") || value.startsWith("https://")) {
|
|
160
|
+
const match = new URL(value).pathname.match(/^\/space\/([^/]+)/);
|
|
161
|
+
return match?.[1] ? decodeURIComponent(match[1]).replace(/^space-/, "") : "";
|
|
162
|
+
}
|
|
163
|
+
return value.replace(/^space-/, "");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function getNoloSpaceContentKeys(spaceRecord: any) {
|
|
167
|
+
const keys = new Set<string>();
|
|
168
|
+
const contents = spaceRecord?.contents;
|
|
169
|
+
if (!contents || typeof contents !== "object") return keys;
|
|
170
|
+
for (const [entryKey, value] of Object.entries(contents)) {
|
|
171
|
+
keys.add(entryKey);
|
|
172
|
+
if (value && typeof value === "object") {
|
|
173
|
+
const contentKey = (value as any).contentKey;
|
|
174
|
+
if (typeof contentKey === "string" && contentKey.trim()) {
|
|
175
|
+
keys.add(contentKey.trim());
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return keys;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function getNoloComparableUpdatedAt(record: any) {
|
|
183
|
+
const raw = record?.updatedAt ?? record?.updated_at ?? record?.createdAt ?? record?.created;
|
|
184
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return raw;
|
|
185
|
+
if (typeof raw === "string") return Date.parse(raw) || 0;
|
|
186
|
+
return 0;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function normalizeNoloDocReadArgs(args: Record<string, any>) {
|
|
190
|
+
const id = args.id ?? args.doc ?? args.docKey ?? args.pageKey ?? args.key;
|
|
191
|
+
return id == null ? args : { ...args, id };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function buildNoloTableQueryRequest(args: Record<string, any>, currentUserId?: string | null) {
|
|
195
|
+
const tableInput = noloStringArg(args.table ?? args.metaKey);
|
|
196
|
+
if (!tableInput) return null;
|
|
197
|
+
const tableMetaParts = tableInput.startsWith("meta-")
|
|
198
|
+
? tableInput.split("-")
|
|
199
|
+
: [];
|
|
200
|
+
const tenantId = args.tenantId
|
|
201
|
+
?? (tableMetaParts.length >= 3 ? tableMetaParts[1] : currentUserId);
|
|
202
|
+
const tableId = args.tableId
|
|
203
|
+
?? (tableMetaParts.length >= 3 ? tableMetaParts.slice(2).join("-") : tableInput);
|
|
204
|
+
const filters: Record<string, unknown> = {
|
|
205
|
+
...(args.filters && typeof args.filters === "object" ? args.filters : {}),
|
|
206
|
+
};
|
|
207
|
+
const row = noloStringArg(args.row ?? args.rowId);
|
|
208
|
+
if (row) {
|
|
209
|
+
if (row.startsWith("row-")) filters.dbKey = row;
|
|
210
|
+
else filters.rowId = row;
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
tenantId,
|
|
214
|
+
tableId,
|
|
215
|
+
filters,
|
|
216
|
+
columns: args.columns,
|
|
217
|
+
limit: args.limit ?? (row ? 1 : 20),
|
|
218
|
+
offset: args.offset,
|
|
219
|
+
includeBaseFields: args.includeBaseFields,
|
|
220
|
+
sortBy: args.sortBy ?? "updatedAt",
|
|
221
|
+
sortOrder: args.sortOrder === "asc" ? "asc" : "desc",
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function buildNoloWorkspaceCommandArgs(call: { name: string; arguments: string }) {
|
|
226
|
+
const args = parseNoloWorkspaceToolArguments(call.arguments);
|
|
227
|
+
switch (call.name) {
|
|
228
|
+
case "listDialogs": {
|
|
229
|
+
const cliArgs = ["dialog", "list"];
|
|
230
|
+
const limit = noloPositiveIntegerString(args.limit);
|
|
231
|
+
const space = noloStringArg(args.space);
|
|
232
|
+
if (limit) cliArgs.push("--limit", limit);
|
|
233
|
+
if (space) cliArgs.push("--space", space);
|
|
234
|
+
return cliArgs;
|
|
235
|
+
}
|
|
236
|
+
case "readDialog": {
|
|
237
|
+
const dialog = noloStringArg(args.dialog ?? args.dialogId ?? args.id);
|
|
238
|
+
if (!dialog) throw new Error("readDialog requires dialog.");
|
|
239
|
+
const cliArgs = ["dialog", "read", dialog];
|
|
240
|
+
const limit = noloPositiveIntegerString(args.limit);
|
|
241
|
+
if (limit) cliArgs.push(limit);
|
|
242
|
+
return cliArgs;
|
|
243
|
+
}
|
|
244
|
+
case "listAgents": {
|
|
245
|
+
const cliArgs = ["agent", "list"];
|
|
246
|
+
const space = noloStringArg(args.space);
|
|
247
|
+
if (space) cliArgs.push("--space", space);
|
|
248
|
+
if (args.publicOnly === true) cliArgs.push("--public-only");
|
|
249
|
+
return cliArgs;
|
|
250
|
+
}
|
|
251
|
+
case "readAgent": {
|
|
252
|
+
const agent = noloStringArg(args.agent ?? args.agentKey ?? args.id);
|
|
253
|
+
if (!agent) throw new Error("readAgent requires agent.");
|
|
254
|
+
return ["agent", "read", agent];
|
|
255
|
+
}
|
|
256
|
+
case "listSpaces":
|
|
257
|
+
return ["space", "list"];
|
|
258
|
+
case "readSpace": {
|
|
259
|
+
const space = noloStringArg(args.space ?? args.spaceId ?? args.id);
|
|
260
|
+
if (!space) throw new Error("readSpace requires space.");
|
|
261
|
+
const cliArgs = ["space", "read", space];
|
|
262
|
+
const contentKey = noloStringArg(args.contentKey);
|
|
263
|
+
if (contentKey) cliArgs.push("--content-key", contentKey);
|
|
264
|
+
if (args.brief === true) cliArgs.push("--brief");
|
|
265
|
+
return cliArgs;
|
|
266
|
+
}
|
|
267
|
+
case "readDoc": {
|
|
268
|
+
const doc = noloStringArg(args.doc ?? args.docKey ?? args.pageKey ?? args.key);
|
|
269
|
+
if (!doc) throw new Error("readDoc requires doc.");
|
|
270
|
+
return ["doc", "read", doc];
|
|
271
|
+
}
|
|
272
|
+
case "readSkillDoc": {
|
|
273
|
+
const doc = noloStringArg(args.doc ?? args.docKey ?? args.pageKey ?? args.key);
|
|
274
|
+
if (!doc) throw new Error("readSkillDoc requires doc.");
|
|
275
|
+
return ["skill-doc", "read", doc];
|
|
276
|
+
}
|
|
277
|
+
case "queryTableRows": {
|
|
278
|
+
const table = noloStringArg(args.table ?? args.tableId ?? args.metaKey);
|
|
279
|
+
if (!table) throw new Error("queryTableRows requires table.");
|
|
280
|
+
const cliArgs = ["table", "query", "--table", table];
|
|
281
|
+
const limit = noloPositiveIntegerString(args.limit);
|
|
282
|
+
const row = noloStringArg(args.row ?? args.rowId);
|
|
283
|
+
const output = noloStringArg(args.output);
|
|
284
|
+
if (limit) cliArgs.push("--limit", limit);
|
|
285
|
+
if (row) cliArgs.push("--row", row);
|
|
286
|
+
if (output) cliArgs.push("--output", output);
|
|
287
|
+
return cliArgs;
|
|
288
|
+
}
|
|
289
|
+
case "cliWhoami":
|
|
290
|
+
return ["whoami"];
|
|
291
|
+
case "cliDoctor":
|
|
292
|
+
return ["doctor"];
|
|
293
|
+
default:
|
|
294
|
+
throw new Error(`Unsupported Nolo workspace tool: ${call.name}`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async function readNoloProcessStream(readable: ReadableStream<Uint8Array> | null) {
|
|
299
|
+
if (!readable) return "";
|
|
300
|
+
return new Response(readable).text();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
type NoloSpawnProcess = {
|
|
304
|
+
stdout: ReadableStream<Uint8Array> | null;
|
|
305
|
+
stderr: ReadableStream<Uint8Array> | null;
|
|
306
|
+
exited: Promise<number>;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
type NoloSpawn = (options: {
|
|
310
|
+
cmd: string[];
|
|
311
|
+
stdout: "pipe";
|
|
312
|
+
stderr: "pipe";
|
|
313
|
+
env?: Record<string, string | undefined>;
|
|
314
|
+
}) => NoloSpawnProcess;
|
|
315
|
+
|
|
316
|
+
export async function runNoloWorkspaceCliTool(call: {
|
|
317
|
+
name: string;
|
|
318
|
+
arguments: string;
|
|
319
|
+
}, args: {
|
|
320
|
+
cliEntrypoint: string;
|
|
321
|
+
env?: Record<string, string | undefined>;
|
|
322
|
+
metadataKind?: string;
|
|
323
|
+
processExecPath?: string;
|
|
324
|
+
spawn?: NoloSpawn;
|
|
325
|
+
}) {
|
|
326
|
+
const cliArgs = buildNoloWorkspaceCommandArgs(call);
|
|
327
|
+
const spawn = args.spawn ?? (globalThis as any).Bun?.spawn;
|
|
328
|
+
if (typeof spawn !== "function") {
|
|
329
|
+
throw new Error("Nolo workspace CLI tools require a Bun-compatible spawn runtime.");
|
|
330
|
+
}
|
|
331
|
+
const proc = spawn({
|
|
332
|
+
cmd: [args.processExecPath ?? process.execPath, args.cliEntrypoint, ...cliArgs],
|
|
333
|
+
stdout: "pipe",
|
|
334
|
+
stderr: "pipe",
|
|
335
|
+
env: args.env,
|
|
336
|
+
});
|
|
337
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
338
|
+
readNoloProcessStream(proc.stdout),
|
|
339
|
+
readNoloProcessStream(proc.stderr),
|
|
340
|
+
proc.exited,
|
|
341
|
+
]);
|
|
342
|
+
const content = `${stdout}${stderr}`;
|
|
343
|
+
if (exitCode !== 0) {
|
|
344
|
+
throw new Error(content.trim() || `nolo ${cliArgs.join(" ")} exited ${exitCode}`);
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
content,
|
|
348
|
+
metadata: {
|
|
349
|
+
[args.metadataKind ?? "noloWorkspaceTool"]: true,
|
|
350
|
+
command: ["nolo", ...cliArgs].join(" "),
|
|
351
|
+
exitCode,
|
|
352
|
+
},
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export function buildNoloWorkspaceCliToolExecutors(args: {
|
|
357
|
+
cliEntrypoint: string;
|
|
358
|
+
env?: Record<string, string | undefined>;
|
|
359
|
+
metadataKind?: string;
|
|
360
|
+
processExecPath?: string;
|
|
361
|
+
spawn?: NoloSpawn;
|
|
362
|
+
}) {
|
|
363
|
+
const executors: Record<string, (call: { name: string; arguments: string }) => Promise<{
|
|
364
|
+
content: string;
|
|
365
|
+
metadata?: Record<string, unknown>;
|
|
366
|
+
}>> = {};
|
|
367
|
+
for (const toolName of NOLO_WORKSPACE_TOOL_NAMES) {
|
|
368
|
+
executors[toolName] = (call) => runNoloWorkspaceCliTool(call, args);
|
|
369
|
+
}
|
|
370
|
+
return executors;
|
|
371
|
+
}
|
|
@@ -139,7 +139,6 @@ export function resolveLocalRuntimeEnvFromPolicy(
|
|
|
139
139
|
policy.shell?.mode === "worktree";
|
|
140
140
|
return {
|
|
141
141
|
...runtimeEnv,
|
|
142
|
-
...(policy?.workspace?.cwd ? { NOLO_LOCAL_WORKTREE: policy.workspace.cwd } : {}),
|
|
143
142
|
...(enablesWorktreeShell && !runtimeEnv.NOLO_LOCAL_SHELL_MODE
|
|
144
143
|
? { NOLO_LOCAL_SHELL_MODE: "worktree" }
|
|
145
144
|
: {}),
|
|
@@ -161,8 +160,7 @@ function policyRequestsHostedWorkspace(policy: AgentRuntimeToolPolicy | undefine
|
|
|
161
160
|
return (
|
|
162
161
|
policy.shell?.enabled === true ||
|
|
163
162
|
policy.runtimeTools?.some((tool) => tool === "execShell") ||
|
|
164
|
-
workspaceMode === "lease"
|
|
165
|
-
workspaceMode === "task-worktree"
|
|
163
|
+
workspaceMode === "lease"
|
|
166
164
|
);
|
|
167
165
|
}
|
|
168
166
|
|
package/agent-runtime/types.ts
CHANGED
|
@@ -81,7 +81,7 @@ export type AgentRuntimeDecisionInput = {
|
|
|
81
81
|
serverFallbackAvailable: boolean;
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
-
export type AgentRuntimeWorkspaceMode = "none" | "current" | "
|
|
84
|
+
export type AgentRuntimeWorkspaceMode = "none" | "current" | "lease";
|
|
85
85
|
|
|
86
86
|
export type AgentRuntimeShellPolicy = {
|
|
87
87
|
enabled?: boolean;
|
package/agentAliases.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export const PLATFORM_DEMO_USER_ID = "b2e06f801f";
|
|
2
|
+
export const NOLO_DEFAULT_AGENT_ID = "01NOLOAPPBLD000000019KCKT0";
|
|
3
|
+
export const NOLO_DEFAULT_AGENT_KEY = `agent-pub-${NOLO_DEFAULT_AGENT_ID}`;
|
|
2
4
|
export const NOLO_PROJECT_MANAGER_AGENT_ID = "01NOLOPROJMGR00000000MSVGG";
|
|
3
5
|
export const NOLO_PROJECT_MANAGER_AGENT_KEY =
|
|
4
6
|
`agent-${PLATFORM_DEMO_USER_ID}-${NOLO_PROJECT_MANAGER_AGENT_ID}`;
|
|
@@ -19,6 +21,10 @@ export const MIMO_MONTH_AGENT_KEY =
|
|
|
19
21
|
`agent-0e95801d90-${MIMO_MONTH_AGENT_ID}`;
|
|
20
22
|
|
|
21
23
|
const AGENT_ALIAS_TO_KEY: Record<string, string> = {
|
|
24
|
+
nolo: NOLO_DEFAULT_AGENT_KEY,
|
|
25
|
+
default: NOLO_DEFAULT_AGENT_KEY,
|
|
26
|
+
"default-nolo": NOLO_DEFAULT_AGENT_KEY,
|
|
27
|
+
|
|
22
28
|
// Win Codex (高智力终审与架构把关)
|
|
23
29
|
"win-codex": WIN_CODEX_AGENT_KEY,
|
|
24
30
|
"wincodex": WIN_CODEX_AGENT_KEY,
|