@aliceshimada/mica 1.0.5 → 1.1.1
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/CHANGELOG.md +11 -0
- package/dist/src/bun/httpServer.js +1 -1
- package/dist/src/bun/index.js +1 -1
- package/dist/src/cli/doctor.js +3 -3
- package/dist/src/cli/index.js +16 -7
- package/dist/src/mcp/backendTools.js +17 -1
- package/dist/src/mcp/prompts.js +3 -1
- package/dist/src/mcp/toolSchemas.js +6 -0
- package/package.json +1 -1
- package/paclet/Kernel/MMAAgentBridge.wl +105 -40
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.1 - 2026-06-16
|
|
4
|
+
|
|
5
|
+
- Fix `RestartKernelRequest`: kill kernel via `Quit[]` before restarting.
|
|
6
|
+
- Fix `realpathSync` crash when `process.argv[1]` doesn't resolve.
|
|
7
|
+
- Fix `$BridgeNotebookPermissions` memory leak: prune on notebook close.
|
|
8
|
+
|
|
9
|
+
## 1.1.0 - 2026-06-09
|
|
10
|
+
|
|
11
|
+
- Add `mma_kill_kernel` tool: quit a notebook's Wolfram kernel (control agent kernel is protected).
|
|
12
|
+
- Add `mma_restart_kernel` tool: restart a notebook's Wolfram kernel so it can evaluate cells again.
|
|
13
|
+
|
|
3
14
|
## 1.0.5 - 2026-06-09
|
|
4
15
|
|
|
5
16
|
- Fix abort disconnection: give control evaluator a separate kernel via `LinkLaunch` instead of cloning `"Local"`.
|
|
@@ -3,7 +3,7 @@ import http from "node:http";
|
|
|
3
3
|
import { executeBackendMcpTool } from "../mcp/backendTools.js";
|
|
4
4
|
import { renderDashboard } from "./dashboard.js";
|
|
5
5
|
const JSON_BODY_LIMIT_BYTES = 1024 * 1024;
|
|
6
|
-
const DEFAULT_VERSION = "1.
|
|
6
|
+
const DEFAULT_VERSION = "1.1.1";
|
|
7
7
|
export async function createBunHttpApp({ state, host = "127.0.0.1", port, authToken, version = DEFAULT_VERSION }) {
|
|
8
8
|
const runtimeInfo = {
|
|
9
9
|
host,
|
package/dist/src/bun/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { loadRuntimeConfig } from "../runtime/config.js";
|
|
|
8
8
|
import { writeSessionFile } from "../runtime/session.js";
|
|
9
9
|
import { createBunHttpApp } from "./httpServer.js";
|
|
10
10
|
const MCP_SERVER_NAME = "mica-bun";
|
|
11
|
-
const MICA_PACKAGE_VERSION = "1.
|
|
11
|
+
const MICA_PACKAGE_VERSION = "1.1.1";
|
|
12
12
|
export async function startBunRuntime(deps = {}) {
|
|
13
13
|
const config = deps.runtimeConfig ?? loadRuntimeConfig();
|
|
14
14
|
const bridgeOnly = deps.bridgeOnly ?? config.bridgeOnly;
|
package/dist/src/cli/doctor.js
CHANGED
|
@@ -83,7 +83,7 @@ export async function runDoctor(deps = {}) {
|
|
|
83
83
|
let sessionBaseUrl;
|
|
84
84
|
if (!_exists(sessionFile)) {
|
|
85
85
|
fail("Session file", `${sessionFile} (not found)`);
|
|
86
|
-
fix("Run: mica
|
|
86
|
+
fix("Run: mica mcp");
|
|
87
87
|
}
|
|
88
88
|
else {
|
|
89
89
|
try {
|
|
@@ -97,7 +97,7 @@ export async function runDoctor(deps = {}) {
|
|
|
97
97
|
}
|
|
98
98
|
catch (e) {
|
|
99
99
|
fail("Session file", `${sessionFile} (${e instanceof Error ? e.message : String(e)})`);
|
|
100
|
-
fix("Run: mica
|
|
100
|
+
fix("Run: mica mcp");
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
// -----------------------------------------------------------------------
|
|
@@ -173,7 +173,7 @@ export async function runDoctor(deps = {}) {
|
|
|
173
173
|
catch (e) {
|
|
174
174
|
fail("Auth token", "server not reachable");
|
|
175
175
|
fail("Server /status reachable", e instanceof Error ? e.message : String(e));
|
|
176
|
-
fix("Run: mica
|
|
176
|
+
fix("Run: mica mcp");
|
|
177
177
|
fail("Live agent count", "server not reachable");
|
|
178
178
|
fail("Live notebook count", "server not reachable");
|
|
179
179
|
}
|
package/dist/src/cli/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -222,10 +222,19 @@ async function main() {
|
|
|
222
222
|
});
|
|
223
223
|
process.exitCode = exitCode;
|
|
224
224
|
}
|
|
225
|
-
if (process.argv[1]
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
process.
|
|
229
|
-
|
|
230
|
-
|
|
225
|
+
if (process.argv[1]) {
|
|
226
|
+
try {
|
|
227
|
+
const scriptReal = realpathSync(fileURLToPath(import.meta.url));
|
|
228
|
+
const argReal = realpathSync(process.argv[1]);
|
|
229
|
+
if (scriptReal === argReal) {
|
|
230
|
+
main().catch((error) => {
|
|
231
|
+
const message = error instanceof Error ? error.stack ?? error.message : String(error);
|
|
232
|
+
process.stderr.write(`${message}\n`);
|
|
233
|
+
process.exitCode = 1;
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
// process.argv[1] may not resolve (e.g. node -e, shebang edge cases)
|
|
239
|
+
}
|
|
231
240
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { DEFAULT_TIMEOUTS_MS } from "../backend/protocol.js";
|
|
3
|
-
import { abortEvaluationSchema, deleteCellSchema, getCellOutputSchema, insertCellSchema, listCellsSchema, modifyCellSchema, noArgsSchema, readArtifactSchema, readCellSchema, runCellSchema, selectNotebookSchema, saveNotebookSchema, symbolLookupSchema, } from "./toolSchemas.js";
|
|
3
|
+
import { abortEvaluationSchema, deleteCellSchema, getCellOutputSchema, insertCellSchema, killKernelSchema, listCellsSchema, modifyCellSchema, noArgsSchema, readArtifactSchema, readCellSchema, restartKernelSchema, runCellSchema, selectNotebookSchema, saveNotebookSchema, symbolLookupSchema, } from "./toolSchemas.js";
|
|
4
4
|
import { INSERT_ANCHOR_GUIDANCE, notebookToolDescription } from "./descriptions.js";
|
|
5
5
|
import { toolFailure, toolSuccess, withToolErrors } from "./toolResults.js";
|
|
6
6
|
function assertLiveAgent(state) {
|
|
@@ -141,6 +141,22 @@ const queuedNotebookTools = [
|
|
|
141
141
|
timeoutMs: () => DEFAULT_TIMEOUTS_MS.mutation,
|
|
142
142
|
requiresExplicitTarget: true,
|
|
143
143
|
},
|
|
144
|
+
{
|
|
145
|
+
name: "mma_kill_kernel",
|
|
146
|
+
summary: "Quit the Wolfram kernel for a notebook. The control agent kernel is protected and cannot be killed.",
|
|
147
|
+
schema: killKernelSchema.shape,
|
|
148
|
+
permission: "RunCell",
|
|
149
|
+
timeoutMs: () => DEFAULT_TIMEOUTS_MS.mutation,
|
|
150
|
+
requiresExplicitTarget: true,
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: "mma_restart_kernel",
|
|
154
|
+
summary: "Restart the Wolfram kernel for a notebook so it can evaluate cells again.",
|
|
155
|
+
schema: restartKernelSchema.shape,
|
|
156
|
+
permission: "RunCell",
|
|
157
|
+
timeoutMs: () => DEFAULT_TIMEOUTS_MS.mutation,
|
|
158
|
+
requiresExplicitTarget: true,
|
|
159
|
+
},
|
|
144
160
|
{
|
|
145
161
|
name: "mma_get_cell_output",
|
|
146
162
|
summary: "Read output and messages for one Mathematica notebook cell, refreshing completed run status when observed.",
|
package/dist/src/mcp/prompts.js
CHANGED
|
@@ -12,6 +12,8 @@ const TOOL_GUIDE = [
|
|
|
12
12
|
["mma_delete_cell", "Delete an existing cell."],
|
|
13
13
|
["mma_run_cell", "Evaluate one cell and wait for completion or timeout."],
|
|
14
14
|
["mma_abort_evaluation", "Abort a running notebook evaluation."],
|
|
15
|
+
["mma_kill_kernel", "Quit the Wolfram kernel for a notebook (control agent kernel is protected)."],
|
|
16
|
+
["mma_restart_kernel", "Restart the Wolfram kernel for a notebook so it can evaluate cells again."],
|
|
15
17
|
["mma_get_cell_output", "Read output and messages produced by one cell; this may refresh completed run status."],
|
|
16
18
|
["mma_read_artifact", "Read large output or message artifacts by byte page; ids may become stale after notebook edits or reruns."],
|
|
17
19
|
["mma_save_notebook", "Save the selected notebook when SaveNotebook permission is granted."],
|
|
@@ -33,7 +35,7 @@ export const MICA_AGENT_INSTRUCTIONS = [
|
|
|
33
35
|
"Tools:",
|
|
34
36
|
...TOOL_GUIDE.map(([name, description]) => `- ${name}: ${description}`),
|
|
35
37
|
].join("\n");
|
|
36
|
-
export function createMicaMcpServer(name, version = "1.
|
|
38
|
+
export function createMicaMcpServer(name, version = "1.1.1") {
|
|
37
39
|
return new McpServer({ name, version }, { instructions: MICA_AGENT_INSTRUCTIONS });
|
|
38
40
|
}
|
|
39
41
|
export function registerMicaPrompts(server) {
|
|
@@ -38,6 +38,12 @@ export const runCellSchema = z.object({
|
|
|
38
38
|
export const abortEvaluationSchema = z.object({
|
|
39
39
|
...notebookSelectorFields
|
|
40
40
|
}).strict();
|
|
41
|
+
export const killKernelSchema = z.object({
|
|
42
|
+
...notebookSelectorFields
|
|
43
|
+
}).strict();
|
|
44
|
+
export const restartKernelSchema = z.object({
|
|
45
|
+
...notebookSelectorFields
|
|
46
|
+
}).strict();
|
|
41
47
|
export const getCellOutputSchema = z.object({
|
|
42
48
|
...notebookSelectorFields,
|
|
43
49
|
cellId: z.string().min(1),
|
package/package.json
CHANGED
|
@@ -122,11 +122,31 @@ If[!AssociationQ[Quiet @ Check[$BridgePermissions, None]],
|
|
|
122
122
|
$BridgePermissions = $DefaultBridgePermissions
|
|
123
123
|
];
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
125
|
+
If[!AssociationQ[Quiet @ Check[$BridgeNotebookPermissions, None]],
|
|
126
|
+
$BridgeNotebookPermissions = <||>
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
NotebookPermissions[notebookId_String] := Lookup[$BridgeNotebookPermissions, notebookId, $DefaultBridgePermissions];
|
|
130
|
+
|
|
131
|
+
SetNotebookPermissions[notebookId_String, perms_Association] := (
|
|
132
|
+
$BridgeNotebookPermissions[notebookId] = perms
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
PalettePermissionRow[label_String, key_String, notebookId_:None] := Module[{perms, setter},
|
|
136
|
+
perms = If[StringQ[notebookId] && StringLength[notebookId] > 0,
|
|
137
|
+
NotebookPermissions[notebookId],
|
|
138
|
+
$BridgePermissions
|
|
139
|
+
];
|
|
140
|
+
setter = If[StringQ[notebookId] && StringLength[notebookId] > 0,
|
|
141
|
+
Function[val, SetNotebookPermissions[notebookId, Join[perms, <|key -> val|>]]],
|
|
142
|
+
Function[val, $BridgePermissions[key] = val; Quiet @ Check[PostPermissions[], Null]]
|
|
143
|
+
];
|
|
144
|
+
Row[{
|
|
145
|
+
Checkbox[Dynamic[perms[key], (setter[#]) &]],
|
|
146
|
+
Spacer[8],
|
|
147
|
+
Style[label, 11]
|
|
148
|
+
}]
|
|
149
|
+
];
|
|
130
150
|
|
|
131
151
|
PaletteStatusSummary[] := Module[{server, paletteConnected, notebookAttached, pendingRequests, attachedNotebook, error},
|
|
132
152
|
server = Lookup[$LastStatus, "server", "unknown"];
|
|
@@ -332,14 +352,14 @@ PermissionsPanel[] := Panel[
|
|
|
332
352
|
Column[
|
|
333
353
|
{
|
|
334
354
|
Style["Permissions", Bold],
|
|
335
|
-
Grid[
|
|
355
|
+
Dynamic @ Grid[
|
|
336
356
|
{
|
|
337
|
-
{PalettePermissionRow["Read notebook", "ReadNotebook"]},
|
|
338
|
-
{PalettePermissionRow["Insert cell", "InsertCell"]},
|
|
339
|
-
{PalettePermissionRow["Modify cell", "ModifyCell"]},
|
|
340
|
-
{PalettePermissionRow["Delete cell", "DeleteCell"]},
|
|
341
|
-
{PalettePermissionRow["Run cell", "RunCell"]},
|
|
342
|
-
{PalettePermissionRow["Save notebook", "SaveNotebook"]}
|
|
357
|
+
{PalettePermissionRow["Read notebook", "ReadNotebook", $ActiveNotebookId]},
|
|
358
|
+
{PalettePermissionRow["Insert cell", "InsertCell", $ActiveNotebookId]},
|
|
359
|
+
{PalettePermissionRow["Modify cell", "ModifyCell", $ActiveNotebookId]},
|
|
360
|
+
{PalettePermissionRow["Delete cell", "DeleteCell", $ActiveNotebookId]},
|
|
361
|
+
{PalettePermissionRow["Run cell", "RunCell", $ActiveNotebookId]},
|
|
362
|
+
{PalettePermissionRow["Save notebook", "SaveNotebook", $ActiveNotebookId]}
|
|
343
363
|
},
|
|
344
364
|
Alignment -> Left,
|
|
345
365
|
Spacings -> {1, 0.35}
|
|
@@ -590,10 +610,16 @@ PostPermissions[] := Module[{payload = <|"permissions" -> $BridgePermissions|>},
|
|
|
590
610
|
Quiet @ Check[BridgePost["/permissions", payload], $Failed]
|
|
591
611
|
];
|
|
592
612
|
|
|
593
|
-
NeedsConfirmationQ[action_String] :=
|
|
613
|
+
NeedsConfirmationQ[action_String, notebookId_:None] := Module[{perms},
|
|
614
|
+
perms = If[StringQ[notebookId] && StringLength[notebookId] > 0,
|
|
615
|
+
NotebookPermissions[notebookId],
|
|
616
|
+
$BridgePermissions
|
|
617
|
+
];
|
|
618
|
+
Not @ TrueQ[perms[action]]
|
|
619
|
+
];
|
|
594
620
|
|
|
595
|
-
ConfirmAction[action_String, message_String] := If[
|
|
596
|
-
NeedsConfirmationQ[action],
|
|
621
|
+
ConfirmAction[action_String, message_String, notebookId_:None] := If[
|
|
622
|
+
NeedsConfirmationQ[action, notebookId],
|
|
597
623
|
ChoiceDialog[message, {"Allow" -> True, "Deny" -> False}],
|
|
598
624
|
True
|
|
599
625
|
];
|
|
@@ -606,8 +632,8 @@ FailedRequestCode[failure_Failure] := Module[{code = failure[[1]]},
|
|
|
606
632
|
|
|
607
633
|
FailedRequestMessage[failure_Failure] := Lookup[failure[[2]], "Message", Lookup[failure[[2]], "message", "The Wolfram bridge rejected the request."]];
|
|
608
634
|
|
|
609
|
-
RequireReadPermission[] := If[
|
|
610
|
-
Not @ ConfirmAction["ReadNotebook", "AI requests reading the notebook. Allow?"],
|
|
635
|
+
RequireReadPermission[notebookId_:None] := If[
|
|
636
|
+
Not @ ConfirmAction["ReadNotebook", "AI requests reading the notebook. Allow?", notebookId],
|
|
611
637
|
$Canceled,
|
|
612
638
|
True
|
|
613
639
|
];
|
|
@@ -628,7 +654,7 @@ NotebookInfo[nb_NotebookObject, notebookId_String] := <|
|
|
|
628
654
|
"wolframVersion" -> ToString @ $VersionNumber,
|
|
629
655
|
"platform" -> $OperatingSystem,
|
|
630
656
|
"paletteId" -> $PaletteId,
|
|
631
|
-
"permissions" ->
|
|
657
|
+
"permissions" -> NotebookPermissions[notebookId]
|
|
632
658
|
|>;
|
|
633
659
|
|
|
634
660
|
NotebookRecord[notebookId_String] := Lookup[$BridgeNotebooks, notebookId, <||>];
|
|
@@ -1137,9 +1163,9 @@ RefreshCellMap[notebookId_String] := Module[{record, nb, cells, idByCell, previo
|
|
|
1137
1163
|
payload
|
|
1138
1164
|
];
|
|
1139
1165
|
|
|
1140
|
-
ReadCellById[args_Association] := Module[{notebookId, record, cellId, cell, maxBytes, payload},
|
|
1141
|
-
|
|
1142
|
-
notebookId
|
|
1166
|
+
ReadCellById[args_Association] := Module[{notebookId, record, cellId, cell, maxBytes, payload},
|
|
1167
|
+
notebookId = TargetNotebookId[args];
|
|
1168
|
+
If[RequireReadPermission[notebookId] === $Canceled, Return[$Canceled]];
|
|
1143
1169
|
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1144
1170
|
record = NotebookRecord[notebookId];
|
|
1145
1171
|
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
@@ -1158,9 +1184,9 @@ ReadCellById[args_Association] := Module[{notebookId, record, cellId, cell, maxB
|
|
|
1158
1184
|
]
|
|
1159
1185
|
];
|
|
1160
1186
|
|
|
1161
|
-
GetCellOutputById[args_Association] := Module[{notebookId, record, cellId, cell, artifacts, maxBytes, payload},
|
|
1162
|
-
|
|
1163
|
-
notebookId
|
|
1187
|
+
GetCellOutputById[args_Association] := Module[{notebookId, record, cellId, cell, artifacts, maxBytes, payload},
|
|
1188
|
+
notebookId = TargetNotebookId[args];
|
|
1189
|
+
If[RequireReadPermission[notebookId] === $Canceled, Return[$Canceled]];
|
|
1164
1190
|
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1165
1191
|
record = NotebookRecord[notebookId];
|
|
1166
1192
|
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
@@ -1178,9 +1204,9 @@ GetCellOutputById[args_Association] := Module[{notebookId, record, cellId, cell,
|
|
|
1178
1204
|
]
|
|
1179
1205
|
];
|
|
1180
1206
|
|
|
1181
|
-
ReadArtifactById[args_Association] := Module[{notebookId, record, artifactId, parts, cellId, kind, indexText, index, cell, artifacts, artifactList, text, offset, limit, page, nextOffset, done},
|
|
1182
|
-
|
|
1183
|
-
notebookId
|
|
1207
|
+
ReadArtifactById[args_Association] := Module[{notebookId, record, artifactId, parts, cellId, kind, indexText, index, cell, artifacts, artifactList, text, offset, limit, page, nextOffset, done},
|
|
1208
|
+
notebookId = TargetNotebookId[args];
|
|
1209
|
+
If[RequireReadPermission[notebookId] === $Canceled, Return[$Canceled]];
|
|
1184
1210
|
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1185
1211
|
record = NotebookRecord[notebookId];
|
|
1186
1212
|
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
@@ -1255,8 +1281,8 @@ InsertCellAtBeginning[notebook_NotebookObject, newCell_] := Module[{beforeCount,
|
|
|
1255
1281
|
];
|
|
1256
1282
|
|
|
1257
1283
|
InsertCellRequest[args_Association] := Module[{notebookId, record, afterId, style, content, newCell, notebook, anchor, cells, inserted, refreshed},
|
|
1258
|
-
If[Not @ ConfirmAction["InsertCell", "AI requests inserting 1 cell. Allow?"], Return[$Canceled]];
|
|
1259
1284
|
notebookId = TargetNotebookId[args];
|
|
1285
|
+
If[Not @ ConfirmAction["InsertCell", "AI requests inserting 1 cell. Allow?", notebookId], Return[$Canceled]];
|
|
1260
1286
|
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1261
1287
|
record = NotebookRecord[notebookId];
|
|
1262
1288
|
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
@@ -1291,8 +1317,8 @@ InsertCellRequest[args_Association] := Module[{notebookId, record, afterId, styl
|
|
|
1291
1317
|
];
|
|
1292
1318
|
|
|
1293
1319
|
ModifyCellRequest[args_Association] := Module[{notebookId, record, notebook, cellId, cellMap, cell, content, style, newCell, cells, cellIndex, updatedCells, updatedCell, writeResult},
|
|
1294
|
-
If[Not @ ConfirmAction["ModifyCell", "AI requests modifying 1 cell. Allow?"], Return[$Canceled]];
|
|
1295
1320
|
notebookId = TargetNotebookId[args];
|
|
1321
|
+
If[Not @ ConfirmAction["ModifyCell", "AI requests modifying 1 cell. Allow?", notebookId], Return[$Canceled]];
|
|
1296
1322
|
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1297
1323
|
record = NotebookRecord[notebookId];
|
|
1298
1324
|
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
@@ -1322,8 +1348,8 @@ ModifyCellRequest[args_Association] := Module[{notebookId, record, notebook, cel
|
|
|
1322
1348
|
];
|
|
1323
1349
|
|
|
1324
1350
|
DeleteCellRequest[args_Association] := Module[{notebookId, record, notebook, cellId, cell, cellMap, artifacts, artifactIds, newCellMap},
|
|
1325
|
-
If[Not @ ConfirmAction["DeleteCell", "AI requests deleting 1 cell. Allow?"], Return[$Canceled]];
|
|
1326
1351
|
notebookId = TargetNotebookId[args];
|
|
1352
|
+
If[Not @ ConfirmAction["DeleteCell", "AI requests deleting 1 cell. Allow?", notebookId], Return[$Canceled]];
|
|
1327
1353
|
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1328
1354
|
record = NotebookRecord[notebookId];
|
|
1329
1355
|
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
@@ -1345,8 +1371,8 @@ DeleteCellRequest[args_Association] := Module[{notebookId, record, notebook, cel
|
|
|
1345
1371
|
];
|
|
1346
1372
|
|
|
1347
1373
|
RunCellRequest[args_Association] := Module[{notebookId, record, notebook, cellId, cell, timeoutSec = Lookup[args, "timeoutSec", 120], installedEpilog, evaluateResult},
|
|
1348
|
-
If[Not @ ConfirmAction["RunCell", "AI requests running 1 cell. Allow?"], Return[$Canceled]];
|
|
1349
1374
|
notebookId = TargetNotebookId[args];
|
|
1375
|
+
If[Not @ ConfirmAction["RunCell", "AI requests running 1 cell. Allow?", notebookId], Return[$Canceled]];
|
|
1350
1376
|
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1351
1377
|
record = NotebookRecord[notebookId];
|
|
1352
1378
|
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
@@ -1384,8 +1410,8 @@ RunCellRequest[args_Association] := Module[{notebookId, record, notebook, cellId
|
|
|
1384
1410
|
];
|
|
1385
1411
|
|
|
1386
1412
|
AbortEvaluationRequest[args_Association] := Module[{notebookId, record, notebook, runningCellId, runningRequestId, wasRunning},
|
|
1387
|
-
If[Not @ ConfirmAction["RunCell", "AI requests aborting the running evaluation. Allow?"], Return[$Canceled]];
|
|
1388
1413
|
notebookId = TargetNotebookId[args];
|
|
1414
|
+
If[Not @ ConfirmAction["RunCell", "AI requests aborting the running evaluation. Allow?", notebookId], Return[$Canceled]];
|
|
1389
1415
|
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1390
1416
|
record = NotebookRecord[notebookId];
|
|
1391
1417
|
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
@@ -1407,11 +1433,43 @@ AbortEvaluationRequest[args_Association] := Module[{notebookId, record, notebook
|
|
|
1407
1433
|
<|"status" -> "abort_requested", "cellId" -> runningCellId, "requestId" -> runningRequestId|>,
|
|
1408
1434
|
<|"status" -> "idle"|>
|
|
1409
1435
|
]
|
|
1410
|
-
];
|
|
1436
|
+
];
|
|
1437
|
+
|
|
1438
|
+
KillKernelRequest[args_Association] := Module[{notebookId, record, notebook, evaluatorName},
|
|
1439
|
+
notebookId = TargetNotebookId[args];
|
|
1440
|
+
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1441
|
+
record = NotebookRecord[notebookId];
|
|
1442
|
+
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1443
|
+
notebook = Lookup[record, "notebook", None];
|
|
1444
|
+
If[Head[notebook] =!= NotebookObject, Return[Failure["BAD_REQUEST", <|"message" -> "Notebook is unavailable."|>]]];
|
|
1445
|
+
evaluatorName = Quiet @ Check[CurrentValue[notebook, Evaluator], $ControlAgentEvaluatorName];
|
|
1446
|
+
If[evaluatorName === $ControlAgentEvaluatorName,
|
|
1447
|
+
Return[Failure["PROTECTED_EVALUATOR", <|"message" -> "Cannot kill the MICA control agent evaluator."|>]]
|
|
1448
|
+
];
|
|
1449
|
+
Quiet @ Check[NotebookEvaluate[notebook, "Quit[]", InsertResults -> False], Null];
|
|
1450
|
+
<|"status" -> "killed", "notebookId" -> notebookId|>
|
|
1451
|
+
];
|
|
1452
|
+
|
|
1453
|
+
RestartKernelRequest[args_Association] := Module[{notebookId, record, notebook, evaluatorName},
|
|
1454
|
+
notebookId = TargetNotebookId[args];
|
|
1455
|
+
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1456
|
+
record = NotebookRecord[notebookId];
|
|
1457
|
+
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1458
|
+
notebook = Lookup[record, "notebook", None];
|
|
1459
|
+
If[Head[notebook] =!= NotebookObject, Return[Failure["BAD_REQUEST", <|"message" -> "Notebook is unavailable."|>]]];
|
|
1460
|
+
evaluatorName = Quiet @ Check[CurrentValue[notebook, Evaluator], $ControlAgentEvaluatorName];
|
|
1461
|
+
If[evaluatorName === $ControlAgentEvaluatorName,
|
|
1462
|
+
Return[Failure["PROTECTED_EVALUATOR", <|"message" -> "Cannot restart the MICA control agent evaluator."|>]]
|
|
1463
|
+
];
|
|
1464
|
+
Quiet @ Check[NotebookEvaluate[notebook, "Quit[]", InsertResults -> False], Null];
|
|
1465
|
+
Pause[0.5];
|
|
1466
|
+
Quiet @ Check[NotebookEvaluate[notebook, "Null", InsertResults -> False], Null];
|
|
1467
|
+
<|"status" -> "restarted", "notebookId" -> notebookId|>
|
|
1468
|
+
];
|
|
1411
1469
|
|
|
1412
1470
|
SaveNotebookRequest[args_Association] := Module[{notebookId, record, notebook},
|
|
1413
|
-
If[Not @ ConfirmAction["SaveNotebook", "AI requests saving the notebook. Allow?"], Return[$Canceled]];
|
|
1414
1471
|
notebookId = TargetNotebookId[args];
|
|
1472
|
+
If[Not @ ConfirmAction["SaveNotebook", "AI requests saving the notebook. Allow?", notebookId], Return[$Canceled]];
|
|
1415
1473
|
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1416
1474
|
record = NotebookRecord[notebookId];
|
|
1417
1475
|
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
@@ -1466,11 +1524,15 @@ AgentHeartbeat[] := BridgePost[
|
|
|
1466
1524
|
|>
|
|
1467
1525
|
];
|
|
1468
1526
|
|
|
1469
|
-
NotebookHeartbeatPayload[nb_NotebookObject] := Module[{savedPath, windowTitle, displayName, frontendObjectKey},
|
|
1527
|
+
NotebookHeartbeatPayload[nb_NotebookObject, notebookId_:None] := Module[{savedPath, windowTitle, displayName, frontendObjectKey, perms},
|
|
1470
1528
|
savedPath = Quiet @ Check[ToString[Replace[NotebookFileName[nb], $Failed -> ""]], ""];
|
|
1471
1529
|
frontendObjectKey = FrontendObjectKey[nb];
|
|
1472
1530
|
windowTitle = NotebookWindowTitle[nb];
|
|
1473
1531
|
displayName = NotebookDisplayNameForHeartbeat[nb, savedPath, frontendObjectKey];
|
|
1532
|
+
perms = If[StringQ[notebookId] && StringLength[notebookId] > 0,
|
|
1533
|
+
NotebookPermissions[notebookId],
|
|
1534
|
+
$DefaultBridgePermissions
|
|
1535
|
+
];
|
|
1474
1536
|
<|
|
|
1475
1537
|
"agentSessionId" -> $AgentSessionId,
|
|
1476
1538
|
"frontendObjectKey" -> frontendObjectKey,
|
|
@@ -1480,7 +1542,7 @@ NotebookHeartbeatPayload[nb_NotebookObject] := Module[{savedPath, windowTitle, d
|
|
|
1480
1542
|
"savedPath" -> savedPath,
|
|
1481
1543
|
"wolframVersion" -> ToString[$VersionNumber],
|
|
1482
1544
|
"platform" -> $OperatingSystem,
|
|
1483
|
-
"permissions" ->
|
|
1545
|
+
"permissions" -> perms,
|
|
1484
1546
|
"seenAt" -> UnixTimeMilliseconds[]
|
|
1485
1547
|
|>
|
|
1486
1548
|
];
|
|
@@ -1514,7 +1576,8 @@ AgentHeartbeatNotebookClosure[notebookId_String] := Module[{record = Lookup[$Bri
|
|
|
1514
1576
|
response = Quiet @ Check[BridgePost["/notebooks/" <> URLComponentEncodeString[notebookId] <> "/closed", <|"agentSessionId" -> $AgentSessionId|>], $Failed];
|
|
1515
1577
|
If[AssociationQ[record] && AssociationQ[response] && TrueQ[Lookup[response, "ok", False]],
|
|
1516
1578
|
If[!TrueQ[Lookup[record, "closed", False]],
|
|
1517
|
-
$BridgeNotebooks[notebookId] = Join[record, <|"closed" -> True|>]
|
|
1579
|
+
$BridgeNotebooks[notebookId] = Join[record, <|"closed" -> True|>];
|
|
1580
|
+
KeyDropFrom[$BridgeNotebookPermissions, notebookId]
|
|
1518
1581
|
]
|
|
1519
1582
|
];
|
|
1520
1583
|
response
|
|
@@ -1538,7 +1601,7 @@ HeartbeatNotebooks[] := Module[{notebooks = AgentVisibleNotebooks[], visibleNote
|
|
|
1538
1601
|
None
|
|
1539
1602
|
];
|
|
1540
1603
|
If[StringQ[localNotebookId] && StringLength[localNotebookId] > 0, AppendTo[visibleNotebookIds, localNotebookId]];
|
|
1541
|
-
payload = NotebookHeartbeatPayload[nb];
|
|
1604
|
+
payload = NotebookHeartbeatPayload[nb, localNotebookId];
|
|
1542
1605
|
response = Quiet @ Check[BridgePost["/notebooks/heartbeat", payload], $Failed];
|
|
1543
1606
|
If[AssociationQ[response],
|
|
1544
1607
|
notebookId = Lookup[Lookup[response, "notebook", <||>], "notebookId", Lookup[response, "notebookId", None]];
|
|
@@ -1771,13 +1834,15 @@ ExecuteRequest[request_Association] := Module[{requestId, tool, args, result},
|
|
|
1771
1834
|
!AssociationQ[args], Failure["BAD_REQUEST", <|"Message" -> "Request arguments must be an association."|>],
|
|
1772
1835
|
True,
|
|
1773
1836
|
Switch[tool,
|
|
1774
|
-
"mma_list_cells", If[RequireReadPermission[] === $Canceled, $Canceled, Module[{notebookId = TargetNotebookId[args], refresh}, If[!StringQ[notebookId] || StringLength[notebookId] == 0, Failure["BAD_REQUEST", <|"Message" -> "No notebook is selected."|>], refresh = RefreshCellMap[notebookId]; If[MatchQ[refresh, _Failure], refresh, <|"cells" -> refresh|>]]]],
|
|
1837
|
+
"mma_list_cells", If[RequireReadPermission[TargetNotebookId[args]] === $Canceled, $Canceled, Module[{notebookId = TargetNotebookId[args], refresh}, If[!StringQ[notebookId] || StringLength[notebookId] == 0, Failure["BAD_REQUEST", <|"Message" -> "No notebook is selected."|>], refresh = RefreshCellMap[notebookId]; If[MatchQ[refresh, _Failure], refresh, <|"cells" -> refresh|>]]]],
|
|
1775
1838
|
"mma_read_cell", ReadCellById[args],
|
|
1776
1839
|
"mma_insert_cell", InsertCellRequest[args],
|
|
1777
1840
|
"mma_modify_cell", ModifyCellRequest[args],
|
|
1778
1841
|
"mma_delete_cell", DeleteCellRequest[args],
|
|
1779
1842
|
"mma_run_cell", RunCellRequest[args],
|
|
1780
|
-
"mma_abort_evaluation", AbortEvaluationRequest[args],
|
|
1843
|
+
"mma_abort_evaluation", AbortEvaluationRequest[args],
|
|
1844
|
+
"mma_kill_kernel", KillKernelRequest[args],
|
|
1845
|
+
"mma_restart_kernel", RestartKernelRequest[args],
|
|
1781
1846
|
"mma_get_cell_output", GetCellOutputById[args],
|
|
1782
1847
|
"mma_read_artifact", ReadArtifactById[args],
|
|
1783
1848
|
"mma_save_notebook", SaveNotebookRequest[args],
|