@aliceshimada/mica 1.1.1 → 1.2.0
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 +7 -0
- package/dist/src/bun/httpServer.js +2 -2
- package/dist/src/bun/index.js +1 -1
- package/dist/src/mcp/backendTools.js +33 -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 +370 -329
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.0 - 2026-06-16
|
|
4
|
+
|
|
5
|
+
- Add `mma_create_notebook` tool: create a new blank notebook in the Wolfram FrontEnd.
|
|
6
|
+
- Add `mma_open_notebook` tool: open an existing notebook file (.nb) from disk.
|
|
7
|
+
- Add `CreateNotebook` and `OpenNotebook` permissions (default false).
|
|
8
|
+
- Fix agent-level tools when no live notebook exists for routing.
|
|
9
|
+
|
|
3
10
|
## 1.1.1 - 2026-06-16
|
|
4
11
|
|
|
5
12
|
- Fix `RestartKernelRequest`: kill kernel via `Quit[]` before restarting.
|
|
@@ -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.2.0";
|
|
7
7
|
export async function createBunHttpApp({ state, host = "127.0.0.1", port, authToken, version = DEFAULT_VERSION }) {
|
|
8
8
|
const runtimeInfo = {
|
|
9
9
|
host,
|
|
@@ -338,7 +338,7 @@ function readPermissions(value) {
|
|
|
338
338
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
339
339
|
throw new Error("BAD_REQUEST");
|
|
340
340
|
const record = value;
|
|
341
|
-
const keys = ["ReadNotebook", "InsertCell", "ModifyCell", "DeleteCell", "RunCell", "SaveNotebook"];
|
|
341
|
+
const keys = ["ReadNotebook", "InsertCell", "ModifyCell", "DeleteCell", "RunCell", "SaveNotebook", "CreateNotebook", "OpenNotebook"];
|
|
342
342
|
const permissions = {};
|
|
343
343
|
for (const key of keys) {
|
|
344
344
|
if (typeof record[key] !== "boolean")
|
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.2.0";
|
|
12
12
|
export async function startBunRuntime(deps = {}) {
|
|
13
13
|
const config = deps.runtimeConfig ?? loadRuntimeConfig();
|
|
14
14
|
const bridgeOnly = deps.bridgeOnly ?? config.bridgeOnly;
|
|
@@ -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, killKernelSchema, listCellsSchema, modifyCellSchema, noArgsSchema, readArtifactSchema, readCellSchema, restartKernelSchema, runCellSchema, selectNotebookSchema, saveNotebookSchema, symbolLookupSchema, } from "./toolSchemas.js";
|
|
3
|
+
import { abortEvaluationSchema, createNotebookSchema, deleteCellSchema, getCellOutputSchema, insertCellSchema, killKernelSchema, listCellsSchema, modifyCellSchema, noArgsSchema, openNotebookSchema, 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) {
|
|
@@ -196,6 +196,16 @@ export const MICA_BACKEND_TOOL_DEFINITIONS = [
|
|
|
196
196
|
description: notebookToolDescription("Select the active Mathematica notebook in the backend registry."),
|
|
197
197
|
schema: selectNotebookSchema.shape,
|
|
198
198
|
},
|
|
199
|
+
{
|
|
200
|
+
name: "mma_create_notebook",
|
|
201
|
+
description: notebookToolDescription("Create a new blank notebook in the Wolfram FrontEnd with the given window title."),
|
|
202
|
+
schema: createNotebookSchema.shape,
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: "mma_open_notebook",
|
|
206
|
+
description: notebookToolDescription("Open an existing notebook file (.nb) from disk in the Wolfram FrontEnd."),
|
|
207
|
+
schema: openNotebookSchema.shape,
|
|
208
|
+
},
|
|
199
209
|
...queuedNotebookTools.map((config) => ({
|
|
200
210
|
name: config.name,
|
|
201
211
|
description: notebookToolDescription(config.summary, config.extraGuidance),
|
|
@@ -231,6 +241,28 @@ export async function executeBackendMcpTool(state, tool, args = {}, extra) {
|
|
|
231
241
|
return toolSuccess({ activeNotebookId: state.activeNotebookId, notebook: target.notebook });
|
|
232
242
|
});
|
|
233
243
|
}
|
|
244
|
+
// Agent-level tools: route to any live agent without requiring a notebook target
|
|
245
|
+
if (tool === "mma_create_notebook" || tool === "mma_open_notebook") {
|
|
246
|
+
return withToolErrors({ tool, args: recordArgs }, () => {
|
|
247
|
+
sweepStateLiveness(state);
|
|
248
|
+
const live = state.agents.list().find((a) => !a.offline && !a.retired);
|
|
249
|
+
if (!live)
|
|
250
|
+
throw new Error("NO_LIVE_AGENT");
|
|
251
|
+
const notebook = state.notebooks.listLive().find((n) => n.agentSessionId === live.agentSessionId);
|
|
252
|
+
const targetNotebookId = notebook?.notebookId ?? `__agent__${live.agentSessionId}`;
|
|
253
|
+
const requestId = randomUUID();
|
|
254
|
+
state.queue.enqueue({
|
|
255
|
+
requestId,
|
|
256
|
+
tool,
|
|
257
|
+
arguments: recordArgs,
|
|
258
|
+
targetNotebookId,
|
|
259
|
+
agentSessionId: live.agentSessionId,
|
|
260
|
+
timeoutMs: DEFAULT_TIMEOUTS_MS.mutation,
|
|
261
|
+
createdAt: Date.now(),
|
|
262
|
+
});
|
|
263
|
+
return toolSuccess({ status: "queued", requestId });
|
|
264
|
+
});
|
|
265
|
+
}
|
|
234
266
|
const queuedConfig = queuedNotebookTools.find((config) => config.name === tool);
|
|
235
267
|
if (!queuedConfig) {
|
|
236
268
|
return toolFailure(new Error(`UNKNOWN_TOOL: ${tool}`), { tool, args: recordArgs });
|
package/dist/src/mcp/prompts.js
CHANGED
|
@@ -14,6 +14,8 @@ const TOOL_GUIDE = [
|
|
|
14
14
|
["mma_abort_evaluation", "Abort a running notebook evaluation."],
|
|
15
15
|
["mma_kill_kernel", "Quit the Wolfram kernel for a notebook (control agent kernel is protected)."],
|
|
16
16
|
["mma_restart_kernel", "Restart the Wolfram kernel for a notebook so it can evaluate cells again."],
|
|
17
|
+
["mma_create_notebook", "Create a new blank notebook in the Wolfram FrontEnd."],
|
|
18
|
+
["mma_open_notebook", "Open an existing notebook file (.nb) from disk in the Wolfram FrontEnd."],
|
|
17
19
|
["mma_get_cell_output", "Read output and messages produced by one cell; this may refresh completed run status."],
|
|
18
20
|
["mma_read_artifact", "Read large output or message artifacts by byte page; ids may become stale after notebook edits or reruns."],
|
|
19
21
|
["mma_save_notebook", "Save the selected notebook when SaveNotebook permission is granted."],
|
|
@@ -35,7 +37,7 @@ export const MICA_AGENT_INSTRUCTIONS = [
|
|
|
35
37
|
"Tools:",
|
|
36
38
|
...TOOL_GUIDE.map(([name, description]) => `- ${name}: ${description}`),
|
|
37
39
|
].join("\n");
|
|
38
|
-
export function createMicaMcpServer(name, version = "1.
|
|
40
|
+
export function createMicaMcpServer(name, version = "1.2.0") {
|
|
39
41
|
return new McpServer({ name, version }, { instructions: MICA_AGENT_INSTRUCTIONS });
|
|
40
42
|
}
|
|
41
43
|
export function registerMicaPrompts(server) {
|
|
@@ -44,6 +44,12 @@ export const killKernelSchema = z.object({
|
|
|
44
44
|
export const restartKernelSchema = z.object({
|
|
45
45
|
...notebookSelectorFields
|
|
46
46
|
}).strict();
|
|
47
|
+
export const createNotebookSchema = z.object({
|
|
48
|
+
title: z.string().min(1).describe("Window title for the new notebook")
|
|
49
|
+
}).strict();
|
|
50
|
+
export const openNotebookSchema = z.object({
|
|
51
|
+
path: z.string().min(1).describe("Absolute path to an existing .nb file")
|
|
52
|
+
}).strict();
|
|
47
53
|
export const getCellOutputSchema = z.object({
|
|
48
54
|
...notebookSelectorFields,
|
|
49
55
|
cellId: z.string().min(1),
|
package/package.json
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
(* ::Package:: *)
|
|
2
|
+
|
|
1
3
|
BeginPackage["MMAAgentBridge`"];
|
|
2
4
|
|
|
3
5
|
StartMMAAgentPalette::usage = "StartMMAAgentPalette[] opens the MMA Agent Bridge palette.";
|
|
@@ -15,10 +17,10 @@ PollCancellations::usage = "PollCancellations[] polls palette-originated cancell
|
|
|
15
17
|
|
|
16
18
|
Begin["`Private`"];
|
|
17
19
|
|
|
18
|
-
$DefaultBridgeBaseURL = "http://127.0.0.1:19791";
|
|
19
|
-
$BridgeBaseURL = $DefaultBridgeBaseURL;
|
|
20
|
-
$BridgeAuthToken = None;
|
|
21
|
-
$BridgeSessionFile = Automatic;
|
|
20
|
+
$DefaultBridgeBaseURL = "http://127.0.0.1:19791";
|
|
21
|
+
$BridgeBaseURL = $DefaultBridgeBaseURL;
|
|
22
|
+
$BridgeAuthToken = None;
|
|
23
|
+
$BridgeSessionFile = Automatic;
|
|
22
24
|
$BridgeNotebooks = <||>;
|
|
23
25
|
$ActiveNotebookId = None;
|
|
24
26
|
$PaletteId = CreateUUID["palette-"];
|
|
@@ -33,24 +35,24 @@ $RunningNotebookId = None;
|
|
|
33
35
|
$RunningNotebookObject = None;
|
|
34
36
|
$RunningCellObject = None;
|
|
35
37
|
$RunningCellOriginalEpilogRule = HoldComplete[CellEpilog -> Inherited];
|
|
36
|
-
$RunningCellRestoreEpilogRule = HoldComplete[CellEpilog -> Inherited];
|
|
37
|
-
$RunningStartedAt = None;
|
|
38
|
-
$RunningStatus = None;
|
|
39
|
-
$RunningStatusGraceSeconds = 2.0;
|
|
40
|
-
$RunningTimeoutAt = None;
|
|
41
|
-
$AbortRequestedAt = None;
|
|
42
|
-
(* Reserved for future late-result surfacing; backend queue already rejects late timeout/cancel results. *)
|
|
43
|
-
$LastLateResult = None;
|
|
44
|
-
$LastRunStatusCellId = None;
|
|
38
|
+
$RunningCellRestoreEpilogRule = HoldComplete[CellEpilog -> Inherited];
|
|
39
|
+
$RunningStartedAt = None;
|
|
40
|
+
$RunningStatus = None;
|
|
41
|
+
$RunningStatusGraceSeconds = 2.0;
|
|
42
|
+
$RunningTimeoutAt = None;
|
|
43
|
+
$AbortRequestedAt = None;
|
|
44
|
+
(* Reserved for future late-result surfacing; backend queue already rejects late timeout/cancel results. *)
|
|
45
|
+
$LastLateResult = None;
|
|
46
|
+
$LastRunStatusCellId = None;
|
|
45
47
|
$LastRunStatusNotebookId = None;
|
|
46
48
|
$LastRunStatus = None;
|
|
47
49
|
$LastStatus = <||>;
|
|
48
|
-
$LastError = None;
|
|
49
|
-
$PollingInProgress = False;
|
|
50
|
-
$MaxArtifactScanCells = 20;
|
|
51
|
-
$DefaultMaxCellPayloadBytes = 262144;
|
|
52
|
-
$MaxCellPayloadBytes = 1024 * 1024;
|
|
53
|
-
$BridgeHTTPTimeoutSeconds = 30;
|
|
50
|
+
$LastError = None;
|
|
51
|
+
$PollingInProgress = False;
|
|
52
|
+
$MaxArtifactScanCells = 20;
|
|
53
|
+
$DefaultMaxCellPayloadBytes = 262144;
|
|
54
|
+
$MaxCellPayloadBytes = 1024 * 1024;
|
|
55
|
+
$BridgeHTTPTimeoutSeconds = 30;
|
|
54
56
|
$BridgeHTTPRetryCount = 3;
|
|
55
57
|
$BridgeHTTPRetryDelaySeconds = 0.25;
|
|
56
58
|
$BridgeInbox = {};
|
|
@@ -68,43 +70,43 @@ $MMAAgentBridgeSourceFile = If[StringQ[$InputFileName] && StringLength[$InputFil
|
|
|
68
70
|
$ControlAgentNotebook = None;
|
|
69
71
|
$ControlAgentEvaluatorName = "MMAAgentControl";
|
|
70
72
|
|
|
71
|
-
UnixTimeMilliseconds[] := Round[1000 UnixTime[]];
|
|
72
|
-
|
|
73
|
-
NonEmptyStringQ[value_] := StringQ[value] && StringLength[value] > 0;
|
|
74
|
-
|
|
75
|
-
EnvironmentValue[name_String] := Module[{value},
|
|
76
|
-
value = Quiet @ Check[Environment[name], ""];
|
|
77
|
-
If[NonEmptyStringQ[value], value, ""]
|
|
78
|
-
];
|
|
79
|
-
|
|
80
|
-
DefaultBridgeSessionFile[] := Module[{override, home},
|
|
81
|
-
override = EnvironmentValue["MICA_SESSION_FILE"];
|
|
82
|
-
If[NonEmptyStringQ[override], Return[override]];
|
|
83
|
-
home = EnvironmentValue["HOME"];
|
|
84
|
-
If[!NonEmptyStringQ[home], home = EnvironmentValue["USERPROFILE"]];
|
|
85
|
-
If[!NonEmptyStringQ[home], home = Directory[]];
|
|
86
|
-
FileNameJoin[{home, ".mica", "session.json"}]
|
|
87
|
-
];
|
|
88
|
-
|
|
89
|
-
If[$BridgeSessionFile === Automatic, $BridgeSessionFile = DefaultBridgeSessionFile[]];
|
|
90
|
-
|
|
91
|
-
LoadBridgeSession[] := Module[{sessionFile = $BridgeSessionFile, payload},
|
|
92
|
-
If[!StringQ[sessionFile] || !FileExistsQ[sessionFile], Return[<||>]];
|
|
93
|
-
payload = Quiet @ Check[Import[sessionFile, "RawJSON"], $Failed];
|
|
94
|
-
If[AssociationQ[payload], payload, <||>]
|
|
95
|
-
];
|
|
96
|
-
|
|
97
|
-
ConfigureBridgeFromSession[] := Module[{session, baseUrl, token},
|
|
98
|
-
session = LoadBridgeSession[];
|
|
99
|
-
baseUrl = Lookup[session, "baseUrl", None];
|
|
100
|
-
If[NonEmptyStringQ[baseUrl],
|
|
101
|
-
$BridgeBaseURL = baseUrl,
|
|
102
|
-
$BridgeBaseURL = $DefaultBridgeBaseURL
|
|
103
|
-
];
|
|
104
|
-
token = Lookup[session, "authToken", None];
|
|
105
|
-
$BridgeAuthToken = If[NonEmptyStringQ[token], token, None];
|
|
106
|
-
$BridgeBaseURL
|
|
107
|
-
];
|
|
73
|
+
UnixTimeMilliseconds[] := Round[1000 UnixTime[]];
|
|
74
|
+
|
|
75
|
+
NonEmptyStringQ[value_] := StringQ[value] && StringLength[value] > 0;
|
|
76
|
+
|
|
77
|
+
EnvironmentValue[name_String] := Module[{value},
|
|
78
|
+
value = Quiet @ Check[Environment[name], ""];
|
|
79
|
+
If[NonEmptyStringQ[value], value, ""]
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
DefaultBridgeSessionFile[] := Module[{override, home},
|
|
83
|
+
override = EnvironmentValue["MICA_SESSION_FILE"];
|
|
84
|
+
If[NonEmptyStringQ[override], Return[override]];
|
|
85
|
+
home = EnvironmentValue["HOME"];
|
|
86
|
+
If[!NonEmptyStringQ[home], home = EnvironmentValue["USERPROFILE"]];
|
|
87
|
+
If[!NonEmptyStringQ[home], home = Directory[]];
|
|
88
|
+
FileNameJoin[{home, ".mica", "session.json"}]
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
If[$BridgeSessionFile === Automatic, $BridgeSessionFile = DefaultBridgeSessionFile[]];
|
|
92
|
+
|
|
93
|
+
LoadBridgeSession[] := Module[{sessionFile = $BridgeSessionFile, payload},
|
|
94
|
+
If[!StringQ[sessionFile] || !FileExistsQ[sessionFile], Return[<||>]];
|
|
95
|
+
payload = Quiet @ Check[Import[sessionFile, "RawJSON"], $Failed];
|
|
96
|
+
If[AssociationQ[payload], payload, <||>]
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
ConfigureBridgeFromSession[] := Module[{session, baseUrl, token},
|
|
100
|
+
session = LoadBridgeSession[];
|
|
101
|
+
baseUrl = Lookup[session, "baseUrl", None];
|
|
102
|
+
If[NonEmptyStringQ[baseUrl],
|
|
103
|
+
$BridgeBaseURL = baseUrl,
|
|
104
|
+
$BridgeBaseURL = $DefaultBridgeBaseURL
|
|
105
|
+
];
|
|
106
|
+
token = Lookup[session, "authToken", None];
|
|
107
|
+
$BridgeAuthToken = If[NonEmptyStringQ[token], token, None];
|
|
108
|
+
$BridgeBaseURL
|
|
109
|
+
];
|
|
108
110
|
|
|
109
111
|
(* $BridgePermissions replaces the plan's $Permissions name because *)
|
|
110
112
|
(* $Permissions is a protected Wolfram built-in symbol (Set::wrsym). *)
|
|
@@ -115,8 +117,10 @@ $DefaultBridgePermissions = <|
|
|
|
115
117
|
"ModifyCell" -> False,
|
|
116
118
|
"DeleteCell" -> False,
|
|
117
119
|
"RunCell" -> False,
|
|
118
|
-
"SaveNotebook" -> False
|
|
119
|
-
|
|
120
|
+
"SaveNotebook" -> False,
|
|
121
|
+
"CreateNotebook" -> False,
|
|
122
|
+
"OpenNotebook" -> False
|
|
123
|
+
|>;
|
|
120
124
|
|
|
121
125
|
If[!AssociationQ[Quiet @ Check[$BridgePermissions, None]],
|
|
122
126
|
$BridgePermissions = $DefaultBridgePermissions
|
|
@@ -201,7 +205,7 @@ NotebookDisplayName[record_Association] := Module[{info, title, path, notebookId
|
|
|
201
205
|
path = StringTrim[ToString[Lookup[info, "notebookPath", ""]]];
|
|
202
206
|
If[title === "" && path === "",
|
|
203
207
|
If[StringQ[notebookId] && StringLength[notebookId] > 0, notebookId, "Untitled notebook"],
|
|
204
|
-
If[path === "", title, title <> "
|
|
208
|
+
If[path === "", title, title <> " \[LongDash] " <> FileNameTake[path]]
|
|
205
209
|
]
|
|
206
210
|
];
|
|
207
211
|
|
|
@@ -311,7 +315,7 @@ RuntimeStatusCard[] := Module[{paletteConnected, transportMode, executorState, p
|
|
|
311
315
|
Style["Running request: ", Bold],
|
|
312
316
|
ToString[Lookup[runningRequest, "tool", "request"]],
|
|
313
317
|
If[StringQ[Lookup[runningRequest, "requestId", ""]], " (" <> Lookup[runningRequest, "requestId", ""] <> ")", Nothing],
|
|
314
|
-
If[NumberQ[elapsedSeconds], "
|
|
318
|
+
If[NumberQ[elapsedSeconds], " \[Bullet] " <> ToString[elapsedSeconds] <> "s", Nothing]
|
|
315
319
|
},
|
|
316
320
|
Nothing
|
|
317
321
|
]],
|
|
@@ -359,7 +363,9 @@ PermissionsPanel[] := Panel[
|
|
|
359
363
|
{PalettePermissionRow["Modify cell", "ModifyCell", $ActiveNotebookId]},
|
|
360
364
|
{PalettePermissionRow["Delete cell", "DeleteCell", $ActiveNotebookId]},
|
|
361
365
|
{PalettePermissionRow["Run cell", "RunCell", $ActiveNotebookId]},
|
|
362
|
-
{PalettePermissionRow["Save notebook", "SaveNotebook", $ActiveNotebookId]}
|
|
366
|
+
{PalettePermissionRow["Save notebook", "SaveNotebook", $ActiveNotebookId]},
|
|
367
|
+
{PalettePermissionRow["Create notebook", "CreateNotebook", $ActiveNotebookId]},
|
|
368
|
+
{PalettePermissionRow["Open notebook", "OpenNotebook", $ActiveNotebookId]}
|
|
363
369
|
},
|
|
364
370
|
Alignment -> Left,
|
|
365
371
|
Spacings -> {1, 0.35}
|
|
@@ -519,14 +525,14 @@ PaletteView[] := DynamicModule[{},
|
|
|
519
525
|
]
|
|
520
526
|
];
|
|
521
527
|
|
|
522
|
-
(* Re-read the small session file before each request so Wolfram follows MCP restarts and dynamic ports. *)
|
|
523
|
-
BridgeURL[path_String] := ConfigureBridgeFromSession[] <> path;
|
|
524
|
-
|
|
525
|
-
BridgeHeaders[] := If[
|
|
526
|
-
StringQ[$BridgeAuthToken] && StringLength[$BridgeAuthToken] > 0,
|
|
527
|
-
{"Authorization" -> "Bearer " <> $BridgeAuthToken},
|
|
528
|
-
{}
|
|
529
|
-
];
|
|
528
|
+
(* Re-read the small session file before each request so Wolfram follows MCP restarts and dynamic ports. *)
|
|
529
|
+
BridgeURL[path_String] := ConfigureBridgeFromSession[] <> path;
|
|
530
|
+
|
|
531
|
+
BridgeHeaders[] := If[
|
|
532
|
+
StringQ[$BridgeAuthToken] && StringLength[$BridgeAuthToken] > 0,
|
|
533
|
+
{"Authorization" -> "Bearer " <> $BridgeAuthToken},
|
|
534
|
+
{}
|
|
535
|
+
];
|
|
530
536
|
|
|
531
537
|
URLComponentEncodeString[None] := "";
|
|
532
538
|
URLComponentEncodeString[value_] := StringReplace[
|
|
@@ -584,8 +590,8 @@ JsonByteArrayToPayload[body_ByteArray] := Module[{text},
|
|
|
584
590
|
Quiet @ Check[ImportString[text, "RawJSON"], $Failed]
|
|
585
591
|
];
|
|
586
592
|
|
|
587
|
-
BridgeGet[path_String] := Module[{response},
|
|
588
|
-
response = BridgeRequestWithRetries[HTTPRequest[BridgeURL[path], <|"Method" -> "GET", "Headers" -> BridgeHeaders[]|>]];
|
|
593
|
+
BridgeGet[path_String] := Module[{response},
|
|
594
|
+
response = BridgeRequestWithRetries[HTTPRequest[BridgeURL[path], <|"Method" -> "GET", "Headers" -> BridgeHeaders[]|>]];
|
|
589
595
|
If[response === $Failed, Return[$Failed]];
|
|
590
596
|
JsonByteArrayToPayload[response["BodyByteArray"]]
|
|
591
597
|
];
|
|
@@ -595,9 +601,9 @@ BridgePost[path_String, payload_Association] := Module[{response},
|
|
|
595
601
|
HTTPRequest[
|
|
596
602
|
BridgeURL[path],
|
|
597
603
|
<|
|
|
598
|
-
"Method" -> "POST",
|
|
599
|
-
"Headers" -> BridgeHeaders[],
|
|
600
|
-
"ContentType" -> "application/json; charset=utf-8",
|
|
604
|
+
"Method" -> "POST",
|
|
605
|
+
"Headers" -> BridgeHeaders[],
|
|
606
|
+
"ContentType" -> "application/json; charset=utf-8",
|
|
601
607
|
"Body" -> PayloadToJsonBytes[payload]
|
|
602
608
|
|>
|
|
603
609
|
]
|
|
@@ -825,141 +831,141 @@ CellPayload[cell_CellObject, id_String, index_Integer] := <|
|
|
|
825
831
|
"tags" -> CellTagsList[cell]
|
|
826
832
|
|>;
|
|
827
833
|
|
|
828
|
-
CellGeneratedBoundaryQ[style_String] := MemberQ[{"Input", "Code", "Text", "Section", "Subsection", "Subsubsection", "Title", "Chapter"}, style];
|
|
829
|
-
|
|
830
|
-
CellArtifactStyleQ[style_String] := MemberQ[{"Output", "Print", "Message"}, style];
|
|
831
|
-
|
|
832
|
-
CellPayloadMaxBytes[args_Association] := Module[{value = Lookup[args, "maxBytes", $DefaultMaxCellPayloadBytes]},
|
|
833
|
-
If[IntegerQ[value] && value > 0 && value <= $MaxCellPayloadBytes, value, $DefaultMaxCellPayloadBytes]
|
|
834
|
-
];
|
|
835
|
-
|
|
836
|
-
Utf8LeadByteLength[byte_Integer] := Which[
|
|
837
|
-
byte < 128, 1,
|
|
838
|
-
byte < 224, 2,
|
|
839
|
-
byte < 240, 3,
|
|
840
|
-
byte < 248, 4,
|
|
841
|
-
True, 1
|
|
842
|
-
];
|
|
843
|
-
|
|
844
|
-
Utf8PrefixByteLength[bytes_List, maxBytes_Integer] := Module[{safeLength = Min[maxBytes, Length[bytes]], start, lead, expected},
|
|
845
|
-
If[safeLength <= 0, Return[0]];
|
|
846
|
-
start = safeLength;
|
|
847
|
-
While[start > 1 && bytes[[start]] >= 128 && bytes[[start]] <= 191, start--];
|
|
848
|
-
lead = bytes[[start]];
|
|
849
|
-
expected = Utf8LeadByteLength[lead];
|
|
850
|
-
If[safeLength - start + 1 >= expected, safeLength, Max[0, start - 1]]
|
|
851
|
-
];
|
|
852
|
-
|
|
853
|
-
TruncateStringToUtf8Bytes[text_String, maxBytes_Integer] := Module[{originalBytes, originalByteLength, safeLength, returnedText},
|
|
854
|
-
originalBytes = ToCharacterCode[text, "UTF8"];
|
|
855
|
-
originalByteLength = Length[originalBytes];
|
|
856
|
-
If[maxBytes <= 0,
|
|
857
|
-
Return[<|"value" -> "", "truncated" -> (originalByteLength > 0), "originalByteLength" -> originalByteLength, "returnedByteLength" -> 0|>]
|
|
858
|
-
];
|
|
859
|
-
If[originalByteLength <= maxBytes,
|
|
860
|
-
Return[<|"value" -> text, "truncated" -> False, "originalByteLength" -> originalByteLength, "returnedByteLength" -> originalByteLength|>]
|
|
861
|
-
];
|
|
862
|
-
safeLength = Utf8PrefixByteLength[originalBytes, maxBytes];
|
|
863
|
-
returnedText = If[safeLength > 0, ByteArrayToString[ByteArray[Take[originalBytes, safeLength]], "UTF8"], ""];
|
|
864
|
-
<|"value" -> returnedText, "truncated" -> True, "originalByteLength" -> originalByteLength, "returnedByteLength" -> safeLength|>
|
|
865
|
-
];
|
|
866
|
-
|
|
867
|
-
TruncatePayloadFields[content_String, outputs_List, messages_List, maxBytes_Integer, includeContentQ_] := Module[{remainingByteLength = maxBytes, totalOriginalByteLength = 0, totalReturnedByteLength = 0, anyTruncated = False, truncatedContent = "", truncatedOutputs = {}, truncatedMessages = {}, processString, output, message},
|
|
868
|
-
processString[text_String] := Module[{item = TruncateStringToUtf8Bytes[text, remainingByteLength]},
|
|
869
|
-
totalOriginalByteLength += item["originalByteLength"];
|
|
870
|
-
totalReturnedByteLength += item["returnedByteLength"];
|
|
871
|
-
anyTruncated = anyTruncated || TrueQ[item["truncated"]];
|
|
872
|
-
remainingByteLength = Max[0, remainingByteLength - item["returnedByteLength"]];
|
|
873
|
-
item["value"]
|
|
874
|
-
];
|
|
875
|
-
If[TrueQ[includeContentQ], truncatedContent = processString[content]];
|
|
876
|
-
Do[
|
|
877
|
-
AppendTo[truncatedOutputs, processString[If[StringQ[output], output, ToString[output, InputForm]]]],
|
|
878
|
-
{output, outputs}
|
|
879
|
-
];
|
|
880
|
-
Do[
|
|
881
|
-
AppendTo[truncatedMessages, processString[If[StringQ[message], message, ToString[message, InputForm]]]],
|
|
882
|
-
{message, messages}
|
|
883
|
-
];
|
|
884
|
-
Join[
|
|
885
|
-
If[TrueQ[includeContentQ], <|"content" -> truncatedContent|>, <||>],
|
|
886
|
-
<|
|
|
887
|
-
"outputs" -> truncatedOutputs,
|
|
888
|
-
"messages" -> truncatedMessages,
|
|
889
|
-
"truncated" -> anyTruncated,
|
|
890
|
-
"originalByteLength" -> totalOriginalByteLength,
|
|
891
|
-
"returnedByteLength" -> totalReturnedByteLength
|
|
892
|
-
|>
|
|
893
|
-
]
|
|
894
|
-
];
|
|
895
|
-
|
|
896
|
-
ArtifactId[cellId_String, kind_String, index_Integer] := cellId <> ":" <> kind <> ":" <> ToString[index];
|
|
897
|
-
|
|
898
|
-
ArtifactDescriptor[cellId_String, kind_String, index_Integer, text_String, maxBytes_Integer] := Module[{byteLength, preview},
|
|
899
|
-
byteLength = Length[ToCharacterCode[text, "UTF8"]];
|
|
900
|
-
preview = TruncateStringToUtf8Bytes[text, maxBytes];
|
|
901
|
-
<|
|
|
902
|
-
"artifactId" -> ArtifactId[cellId, kind, index],
|
|
903
|
-
"type" -> kind,
|
|
904
|
-
"index" -> index,
|
|
905
|
-
"byteLength" -> byteLength,
|
|
906
|
-
"preview" -> preview["value"],
|
|
907
|
-
"previewByteLength" -> preview["returnedByteLength"],
|
|
908
|
-
"truncated" -> True
|
|
909
|
-
|>
|
|
910
|
-
];
|
|
911
|
-
|
|
912
|
-
ArtifactPayloadFields[cellId_String, outputs_List, messages_List, maxBytes_Integer] := Module[{remainingByteLength = maxBytes, totalOriginalByteLength = 0, totalReturnedByteLength = 0, anyTruncated = False, processedOutputs = {}, processedMessages = {}, processString, output, message},
|
|
913
|
-
processString[text_, kind_String, index_Integer] := Module[{value = If[StringQ[text], text, ToString[text, InputForm]], byteLength, descriptor},
|
|
914
|
-
byteLength = Length[ToCharacterCode[value, "UTF8"]];
|
|
915
|
-
totalOriginalByteLength += byteLength;
|
|
916
|
-
If[byteLength <= remainingByteLength,
|
|
917
|
-
totalReturnedByteLength += byteLength;
|
|
918
|
-
remainingByteLength = Max[0, remainingByteLength - byteLength];
|
|
919
|
-
value,
|
|
920
|
-
anyTruncated = True;
|
|
921
|
-
descriptor = ArtifactDescriptor[cellId, kind, index, value, remainingByteLength];
|
|
922
|
-
totalReturnedByteLength += descriptor["previewByteLength"];
|
|
923
|
-
remainingByteLength = Max[0, remainingByteLength - descriptor["previewByteLength"]];
|
|
924
|
-
descriptor
|
|
925
|
-
]
|
|
926
|
-
];
|
|
927
|
-
Do[
|
|
928
|
-
AppendTo[processedOutputs, processString[outputs[[i]], "output", i - 1]],
|
|
929
|
-
{i, Length[outputs]}
|
|
930
|
-
];
|
|
931
|
-
Do[
|
|
932
|
-
AppendTo[processedMessages, processString[messages[[i]], "message", i - 1]],
|
|
933
|
-
{i, Length[messages]}
|
|
934
|
-
];
|
|
935
|
-
<|
|
|
936
|
-
"outputs" -> processedOutputs,
|
|
937
|
-
"messages" -> processedMessages,
|
|
938
|
-
"truncated" -> anyTruncated,
|
|
939
|
-
"originalByteLength" -> totalOriginalByteLength,
|
|
940
|
-
"returnedByteLength" -> totalReturnedByteLength
|
|
941
|
-
|>
|
|
942
|
-
];
|
|
943
|
-
|
|
944
|
-
Utf8SliceStringToBytes[text_String, offset_Integer, limit_Integer] := Module[{bytes, originalByteLength, start, available, safeLength, sliceBytes, value},
|
|
945
|
-
bytes = ToCharacterCode[text, "UTF8"];
|
|
946
|
-
originalByteLength = Length[bytes];
|
|
947
|
-
If[limit <= 0 || offset >= originalByteLength,
|
|
948
|
-
Return[<|"value" -> "", "originalByteLength" -> originalByteLength, "returnedByteLength" -> 0, "nextOffset" -> originalByteLength|>]
|
|
949
|
-
];
|
|
950
|
-
start = Max[1, offset + 1];
|
|
951
|
-
While[start <= originalByteLength && bytes[[start]] >= 128 && bytes[[start]] <= 191, start++];
|
|
952
|
-
available = originalByteLength - start + 1;
|
|
953
|
-
If[available <= 0,
|
|
954
|
-
Return[<|"value" -> "", "originalByteLength" -> originalByteLength, "returnedByteLength" -> 0, "nextOffset" -> originalByteLength|>]
|
|
955
|
-
];
|
|
956
|
-
safeLength = Utf8PrefixByteLength[Take[bytes, {start, originalByteLength}], Min[limit, available]];
|
|
957
|
-
sliceBytes = If[safeLength > 0, Take[bytes, {start, start + safeLength - 1}], {}];
|
|
958
|
-
value = If[safeLength > 0, ByteArrayToString[ByteArray[sliceBytes], "UTF8"], ""];
|
|
959
|
-
<|"value" -> value, "originalByteLength" -> originalByteLength, "returnedByteLength" -> safeLength, "nextOffset" -> (start - 1 + safeLength)|>
|
|
960
|
-
];
|
|
961
|
-
|
|
962
|
-
CellEvaluationTaggingPath[cellId_String] := {TaggingRules, "MMAAgentBridge", "evaluations", cellId, "complete"};
|
|
834
|
+
CellGeneratedBoundaryQ[style_String] := MemberQ[{"Input", "Code", "Text", "Section", "Subsection", "Subsubsection", "Title", "Chapter"}, style];
|
|
835
|
+
|
|
836
|
+
CellArtifactStyleQ[style_String] := MemberQ[{"Output", "Print", "Message"}, style];
|
|
837
|
+
|
|
838
|
+
CellPayloadMaxBytes[args_Association] := Module[{value = Lookup[args, "maxBytes", $DefaultMaxCellPayloadBytes]},
|
|
839
|
+
If[IntegerQ[value] && value > 0 && value <= $MaxCellPayloadBytes, value, $DefaultMaxCellPayloadBytes]
|
|
840
|
+
];
|
|
841
|
+
|
|
842
|
+
Utf8LeadByteLength[byte_Integer] := Which[
|
|
843
|
+
byte < 128, 1,
|
|
844
|
+
byte < 224, 2,
|
|
845
|
+
byte < 240, 3,
|
|
846
|
+
byte < 248, 4,
|
|
847
|
+
True, 1
|
|
848
|
+
];
|
|
849
|
+
|
|
850
|
+
Utf8PrefixByteLength[bytes_List, maxBytes_Integer] := Module[{safeLength = Min[maxBytes, Length[bytes]], start, lead, expected},
|
|
851
|
+
If[safeLength <= 0, Return[0]];
|
|
852
|
+
start = safeLength;
|
|
853
|
+
While[start > 1 && bytes[[start]] >= 128 && bytes[[start]] <= 191, start--];
|
|
854
|
+
lead = bytes[[start]];
|
|
855
|
+
expected = Utf8LeadByteLength[lead];
|
|
856
|
+
If[safeLength - start + 1 >= expected, safeLength, Max[0, start - 1]]
|
|
857
|
+
];
|
|
858
|
+
|
|
859
|
+
TruncateStringToUtf8Bytes[text_String, maxBytes_Integer] := Module[{originalBytes, originalByteLength, safeLength, returnedText},
|
|
860
|
+
originalBytes = ToCharacterCode[text, "UTF8"];
|
|
861
|
+
originalByteLength = Length[originalBytes];
|
|
862
|
+
If[maxBytes <= 0,
|
|
863
|
+
Return[<|"value" -> "", "truncated" -> (originalByteLength > 0), "originalByteLength" -> originalByteLength, "returnedByteLength" -> 0|>]
|
|
864
|
+
];
|
|
865
|
+
If[originalByteLength <= maxBytes,
|
|
866
|
+
Return[<|"value" -> text, "truncated" -> False, "originalByteLength" -> originalByteLength, "returnedByteLength" -> originalByteLength|>]
|
|
867
|
+
];
|
|
868
|
+
safeLength = Utf8PrefixByteLength[originalBytes, maxBytes];
|
|
869
|
+
returnedText = If[safeLength > 0, ByteArrayToString[ByteArray[Take[originalBytes, safeLength]], "UTF8"], ""];
|
|
870
|
+
<|"value" -> returnedText, "truncated" -> True, "originalByteLength" -> originalByteLength, "returnedByteLength" -> safeLength|>
|
|
871
|
+
];
|
|
872
|
+
|
|
873
|
+
TruncatePayloadFields[content_String, outputs_List, messages_List, maxBytes_Integer, includeContentQ_] := Module[{remainingByteLength = maxBytes, totalOriginalByteLength = 0, totalReturnedByteLength = 0, anyTruncated = False, truncatedContent = "", truncatedOutputs = {}, truncatedMessages = {}, processString, output, message},
|
|
874
|
+
processString[text_String] := Module[{item = TruncateStringToUtf8Bytes[text, remainingByteLength]},
|
|
875
|
+
totalOriginalByteLength += item["originalByteLength"];
|
|
876
|
+
totalReturnedByteLength += item["returnedByteLength"];
|
|
877
|
+
anyTruncated = anyTruncated || TrueQ[item["truncated"]];
|
|
878
|
+
remainingByteLength = Max[0, remainingByteLength - item["returnedByteLength"]];
|
|
879
|
+
item["value"]
|
|
880
|
+
];
|
|
881
|
+
If[TrueQ[includeContentQ], truncatedContent = processString[content]];
|
|
882
|
+
Do[
|
|
883
|
+
AppendTo[truncatedOutputs, processString[If[StringQ[output], output, ToString[output, InputForm]]]],
|
|
884
|
+
{output, outputs}
|
|
885
|
+
];
|
|
886
|
+
Do[
|
|
887
|
+
AppendTo[truncatedMessages, processString[If[StringQ[message], message, ToString[message, InputForm]]]],
|
|
888
|
+
{message, messages}
|
|
889
|
+
];
|
|
890
|
+
Join[
|
|
891
|
+
If[TrueQ[includeContentQ], <|"content" -> truncatedContent|>, <||>],
|
|
892
|
+
<|
|
|
893
|
+
"outputs" -> truncatedOutputs,
|
|
894
|
+
"messages" -> truncatedMessages,
|
|
895
|
+
"truncated" -> anyTruncated,
|
|
896
|
+
"originalByteLength" -> totalOriginalByteLength,
|
|
897
|
+
"returnedByteLength" -> totalReturnedByteLength
|
|
898
|
+
|>
|
|
899
|
+
]
|
|
900
|
+
];
|
|
901
|
+
|
|
902
|
+
ArtifactId[cellId_String, kind_String, index_Integer] := cellId <> ":" <> kind <> ":" <> ToString[index];
|
|
903
|
+
|
|
904
|
+
ArtifactDescriptor[cellId_String, kind_String, index_Integer, text_String, maxBytes_Integer] := Module[{byteLength, preview},
|
|
905
|
+
byteLength = Length[ToCharacterCode[text, "UTF8"]];
|
|
906
|
+
preview = TruncateStringToUtf8Bytes[text, maxBytes];
|
|
907
|
+
<|
|
|
908
|
+
"artifactId" -> ArtifactId[cellId, kind, index],
|
|
909
|
+
"type" -> kind,
|
|
910
|
+
"index" -> index,
|
|
911
|
+
"byteLength" -> byteLength,
|
|
912
|
+
"preview" -> preview["value"],
|
|
913
|
+
"previewByteLength" -> preview["returnedByteLength"],
|
|
914
|
+
"truncated" -> True
|
|
915
|
+
|>
|
|
916
|
+
];
|
|
917
|
+
|
|
918
|
+
ArtifactPayloadFields[cellId_String, outputs_List, messages_List, maxBytes_Integer] := Module[{remainingByteLength = maxBytes, totalOriginalByteLength = 0, totalReturnedByteLength = 0, anyTruncated = False, processedOutputs = {}, processedMessages = {}, processString, output, message},
|
|
919
|
+
processString[text_, kind_String, index_Integer] := Module[{value = If[StringQ[text], text, ToString[text, InputForm]], byteLength, descriptor},
|
|
920
|
+
byteLength = Length[ToCharacterCode[value, "UTF8"]];
|
|
921
|
+
totalOriginalByteLength += byteLength;
|
|
922
|
+
If[byteLength <= remainingByteLength,
|
|
923
|
+
totalReturnedByteLength += byteLength;
|
|
924
|
+
remainingByteLength = Max[0, remainingByteLength - byteLength];
|
|
925
|
+
value,
|
|
926
|
+
anyTruncated = True;
|
|
927
|
+
descriptor = ArtifactDescriptor[cellId, kind, index, value, remainingByteLength];
|
|
928
|
+
totalReturnedByteLength += descriptor["previewByteLength"];
|
|
929
|
+
remainingByteLength = Max[0, remainingByteLength - descriptor["previewByteLength"]];
|
|
930
|
+
descriptor
|
|
931
|
+
]
|
|
932
|
+
];
|
|
933
|
+
Do[
|
|
934
|
+
AppendTo[processedOutputs, processString[outputs[[i]], "output", i - 1]],
|
|
935
|
+
{i, Length[outputs]}
|
|
936
|
+
];
|
|
937
|
+
Do[
|
|
938
|
+
AppendTo[processedMessages, processString[messages[[i]], "message", i - 1]],
|
|
939
|
+
{i, Length[messages]}
|
|
940
|
+
];
|
|
941
|
+
<|
|
|
942
|
+
"outputs" -> processedOutputs,
|
|
943
|
+
"messages" -> processedMessages,
|
|
944
|
+
"truncated" -> anyTruncated,
|
|
945
|
+
"originalByteLength" -> totalOriginalByteLength,
|
|
946
|
+
"returnedByteLength" -> totalReturnedByteLength
|
|
947
|
+
|>
|
|
948
|
+
];
|
|
949
|
+
|
|
950
|
+
Utf8SliceStringToBytes[text_String, offset_Integer, limit_Integer] := Module[{bytes, originalByteLength, start, available, safeLength, sliceBytes, value},
|
|
951
|
+
bytes = ToCharacterCode[text, "UTF8"];
|
|
952
|
+
originalByteLength = Length[bytes];
|
|
953
|
+
If[limit <= 0 || offset >= originalByteLength,
|
|
954
|
+
Return[<|"value" -> "", "originalByteLength" -> originalByteLength, "returnedByteLength" -> 0, "nextOffset" -> originalByteLength|>]
|
|
955
|
+
];
|
|
956
|
+
start = Max[1, offset + 1];
|
|
957
|
+
While[start <= originalByteLength && bytes[[start]] >= 128 && bytes[[start]] <= 191, start++];
|
|
958
|
+
available = originalByteLength - start + 1;
|
|
959
|
+
If[available <= 0,
|
|
960
|
+
Return[<|"value" -> "", "originalByteLength" -> originalByteLength, "returnedByteLength" -> 0, "nextOffset" -> originalByteLength|>]
|
|
961
|
+
];
|
|
962
|
+
safeLength = Utf8PrefixByteLength[Take[bytes, {start, originalByteLength}], Min[limit, available]];
|
|
963
|
+
sliceBytes = If[safeLength > 0, Take[bytes, {start, start + safeLength - 1}], {}];
|
|
964
|
+
value = If[safeLength > 0, ByteArrayToString[ByteArray[sliceBytes], "UTF8"], ""];
|
|
965
|
+
<|"value" -> value, "originalByteLength" -> originalByteLength, "returnedByteLength" -> safeLength, "nextOffset" -> (start - 1 + safeLength)|>
|
|
966
|
+
];
|
|
967
|
+
|
|
968
|
+
CellEvaluationTaggingPath[cellId_String] := {TaggingRules, "MMAAgentBridge", "evaluations", cellId, "complete"};
|
|
963
969
|
|
|
964
970
|
MarkCellEvaluationComplete[cellId_String] := Quiet @ Check[
|
|
965
971
|
CurrentValue[EvaluationNotebook[], CellEvaluationTaggingPath[cellId]] = True,
|
|
@@ -1037,23 +1043,23 @@ RestoreRunningCellEpilog[] := Module[{},
|
|
|
1037
1043
|
ClearRunningEvaluationState[] := Module[{cellId = $RunningCellId, notebook = $RunningNotebookObject},
|
|
1038
1044
|
RestoreRunningCellEpilog[];
|
|
1039
1045
|
If[Head[notebook] === NotebookObject && StringQ[cellId], ClearCellEvaluationComplete[notebook, cellId]];
|
|
1040
|
-
$RunningCellId = None;
|
|
1041
|
-
$RunningRequestId = None;
|
|
1042
|
-
$RunningNotebookId = None;
|
|
1043
|
-
$RunningNotebookObject = None;
|
|
1044
|
-
$RunningStartedAt = None;
|
|
1045
|
-
$RunningStatus = None;
|
|
1046
|
-
$AbortRequestedAt = None;
|
|
1047
|
-
$RunningTimeoutAt = None
|
|
1048
|
-
];
|
|
1049
|
-
|
|
1050
|
-
FinishRunningCell[status_String] := Module[{cellId = $RunningCellId, notebookId = $RunningNotebookId},
|
|
1051
|
-
ClearRunningEvaluationState[];
|
|
1052
|
-
$LastRunStatusCellId = cellId;
|
|
1053
|
-
$LastRunStatusNotebookId = notebookId;
|
|
1054
|
-
$RunningStatus = status;
|
|
1055
|
-
$LastRunStatus = status
|
|
1056
|
-
];
|
|
1046
|
+
$RunningCellId = None;
|
|
1047
|
+
$RunningRequestId = None;
|
|
1048
|
+
$RunningNotebookId = None;
|
|
1049
|
+
$RunningNotebookObject = None;
|
|
1050
|
+
$RunningStartedAt = None;
|
|
1051
|
+
$RunningStatus = None;
|
|
1052
|
+
$AbortRequestedAt = None;
|
|
1053
|
+
$RunningTimeoutAt = None
|
|
1054
|
+
];
|
|
1055
|
+
|
|
1056
|
+
FinishRunningCell[status_String] := Module[{cellId = $RunningCellId, notebookId = $RunningNotebookId},
|
|
1057
|
+
ClearRunningEvaluationState[];
|
|
1058
|
+
$LastRunStatusCellId = cellId;
|
|
1059
|
+
$LastRunStatusNotebookId = notebookId;
|
|
1060
|
+
$RunningStatus = status;
|
|
1061
|
+
$LastRunStatus = status
|
|
1062
|
+
];
|
|
1057
1063
|
|
|
1058
1064
|
CellGeneratedArtifactQ[cell_CellObject] := TrueQ[Quiet @ Check[CurrentValue[cell, GeneratedCell], False]] && CellArtifactStyleQ[CellStyleName[cell]];
|
|
1059
1065
|
|
|
@@ -1113,14 +1119,14 @@ CellArtifactScan[cell_CellObject, cellId_String:"", notebook_:Automatic] := Modu
|
|
|
1113
1119
|
"timeout",
|
|
1114
1120
|
StringQ[cellId] && StringQ[$LastRunStatusCellId] && cellId === $LastRunStatusCellId && StringQ[$LastRunStatusNotebookId] && NotebookIdForObject[nb] === $LastRunStatusNotebookId && $LastRunStatus === "finished",
|
|
1115
1121
|
"finished",
|
|
1116
|
-
StringQ[cellId] && StringQ[$LastRunStatusCellId] && cellId === $LastRunStatusCellId && StringQ[$LastRunStatusNotebookId] && NotebookIdForObject[nb] === $LastRunStatusNotebookId && $LastRunStatus === "aborted",
|
|
1117
|
-
"aborted",
|
|
1118
|
-
sameRunningCellQ && NumberQ[$AbortRequestedAt] && !evaluationCompleteQ,
|
|
1119
|
-
"abort_requested",
|
|
1120
|
-
sameRunningCellQ && NumberQ[$AbortRequestedAt] && evaluationCompleteQ,
|
|
1121
|
-
(FinishRunningCell["aborted"]; "aborted"),
|
|
1122
|
-
sameRunningCellQ && NumberQ[$RunningStartedAt] && (AbsoluteTime[] - $RunningStartedAt < $RunningStatusGraceSeconds),
|
|
1123
|
-
"running",
|
|
1122
|
+
StringQ[cellId] && StringQ[$LastRunStatusCellId] && cellId === $LastRunStatusCellId && StringQ[$LastRunStatusNotebookId] && NotebookIdForObject[nb] === $LastRunStatusNotebookId && $LastRunStatus === "aborted",
|
|
1123
|
+
"aborted",
|
|
1124
|
+
sameRunningCellQ && NumberQ[$AbortRequestedAt] && !evaluationCompleteQ,
|
|
1125
|
+
"abort_requested",
|
|
1126
|
+
sameRunningCellQ && NumberQ[$AbortRequestedAt] && evaluationCompleteQ,
|
|
1127
|
+
(FinishRunningCell["aborted"]; "aborted"),
|
|
1128
|
+
sameRunningCellQ && NumberQ[$RunningStartedAt] && (AbsoluteTime[] - $RunningStartedAt < $RunningStatusGraceSeconds),
|
|
1129
|
+
"running",
|
|
1124
1130
|
sameRunningCellQ && (evaluationCompleteQ || hasFinalOutputQ),
|
|
1125
1131
|
(FinishRunningCell["finished"]; "finished"),
|
|
1126
1132
|
sameRunningCellQ,
|
|
@@ -1165,84 +1171,84 @@ RefreshCellMap[notebookId_String] := Module[{record, nb, cells, idByCell, previo
|
|
|
1165
1171
|
|
|
1166
1172
|
ReadCellById[args_Association] := Module[{notebookId, record, cellId, cell, maxBytes, payload},
|
|
1167
1173
|
notebookId = TargetNotebookId[args];
|
|
1168
|
-
If[RequireReadPermission[notebookId] === $Canceled, Return[$Canceled]];
|
|
1169
|
-
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1170
|
-
record = NotebookRecord[notebookId];
|
|
1171
|
-
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1172
|
-
cellId = Lookup[args, "cellId", ""];
|
|
1173
|
-
If[StringLength[cellId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "cellId is required."|>]]];
|
|
1174
|
-
cell = Lookup[Lookup[record, "cellMap", <||>], cellId, Missing["NotFound"]];
|
|
1175
|
-
If[MissingQ[cell], Return[Failure["BAD_REQUEST", <|"message" -> "Requested cell was not found."|>]]];
|
|
1176
|
-
maxBytes = CellPayloadMaxBytes[args];
|
|
1177
|
-
With[{artifacts = CellArtifactScan[cell, cellId, Lookup[record, "notebook", None]]},
|
|
1178
|
-
payload = TruncatePayloadFields[CellContentString[cell], artifacts["outputs"], artifacts["messages"], maxBytes, True];
|
|
1179
|
-
Join[
|
|
1180
|
-
<|"cellId" -> cellId, "style" -> CellStyleName[cell]|>,
|
|
1181
|
-
payload,
|
|
1182
|
-
<|"status" -> artifacts["status"]|>
|
|
1183
|
-
]
|
|
1184
|
-
]
|
|
1185
|
-
];
|
|
1186
|
-
|
|
1174
|
+
If[RequireReadPermission[notebookId] === $Canceled, Return[$Canceled]];
|
|
1175
|
+
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1176
|
+
record = NotebookRecord[notebookId];
|
|
1177
|
+
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1178
|
+
cellId = Lookup[args, "cellId", ""];
|
|
1179
|
+
If[StringLength[cellId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "cellId is required."|>]]];
|
|
1180
|
+
cell = Lookup[Lookup[record, "cellMap", <||>], cellId, Missing["NotFound"]];
|
|
1181
|
+
If[MissingQ[cell], Return[Failure["BAD_REQUEST", <|"message" -> "Requested cell was not found."|>]]];
|
|
1182
|
+
maxBytes = CellPayloadMaxBytes[args];
|
|
1183
|
+
With[{artifacts = CellArtifactScan[cell, cellId, Lookup[record, "notebook", None]]},
|
|
1184
|
+
payload = TruncatePayloadFields[CellContentString[cell], artifacts["outputs"], artifacts["messages"], maxBytes, True];
|
|
1185
|
+
Join[
|
|
1186
|
+
<|"cellId" -> cellId, "style" -> CellStyleName[cell]|>,
|
|
1187
|
+
payload,
|
|
1188
|
+
<|"status" -> artifacts["status"]|>
|
|
1189
|
+
]
|
|
1190
|
+
]
|
|
1191
|
+
];
|
|
1192
|
+
|
|
1187
1193
|
GetCellOutputById[args_Association] := Module[{notebookId, record, cellId, cell, artifacts, maxBytes, payload},
|
|
1188
1194
|
notebookId = TargetNotebookId[args];
|
|
1189
|
-
If[RequireReadPermission[notebookId] === $Canceled, Return[$Canceled]];
|
|
1190
|
-
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1191
|
-
record = NotebookRecord[notebookId];
|
|
1192
|
-
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1195
|
+
If[RequireReadPermission[notebookId] === $Canceled, Return[$Canceled]];
|
|
1196
|
+
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1197
|
+
record = NotebookRecord[notebookId];
|
|
1198
|
+
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1193
1199
|
cellId = Lookup[args, "cellId", ""];
|
|
1194
1200
|
If[StringLength[cellId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "cellId is required."|>]]];
|
|
1195
|
-
cell = Lookup[Lookup[record, "cellMap", <||>], cellId, Missing["NotFound"]];
|
|
1196
|
-
If[MissingQ[cell], Return[Failure["BAD_REQUEST", <|"message" -> "Requested cell was not found."|>]]];
|
|
1197
|
-
maxBytes = CellPayloadMaxBytes[args];
|
|
1198
|
-
artifacts = CellArtifactScan[cell, cellId, Lookup[record, "notebook", None]];
|
|
1199
|
-
payload = ArtifactPayloadFields[cellId, artifacts["outputs"], artifacts["messages"], maxBytes];
|
|
1200
|
-
Join[
|
|
1201
|
-
<|"cellId" -> cellId|>,
|
|
1202
|
-
payload,
|
|
1203
|
-
<|"status" -> artifacts["status"]|>
|
|
1204
|
-
]
|
|
1205
|
-
];
|
|
1206
|
-
|
|
1201
|
+
cell = Lookup[Lookup[record, "cellMap", <||>], cellId, Missing["NotFound"]];
|
|
1202
|
+
If[MissingQ[cell], Return[Failure["BAD_REQUEST", <|"message" -> "Requested cell was not found."|>]]];
|
|
1203
|
+
maxBytes = CellPayloadMaxBytes[args];
|
|
1204
|
+
artifacts = CellArtifactScan[cell, cellId, Lookup[record, "notebook", None]];
|
|
1205
|
+
payload = ArtifactPayloadFields[cellId, artifacts["outputs"], artifacts["messages"], maxBytes];
|
|
1206
|
+
Join[
|
|
1207
|
+
<|"cellId" -> cellId|>,
|
|
1208
|
+
payload,
|
|
1209
|
+
<|"status" -> artifacts["status"]|>
|
|
1210
|
+
]
|
|
1211
|
+
];
|
|
1212
|
+
|
|
1207
1213
|
ReadArtifactById[args_Association] := Module[{notebookId, record, artifactId, parts, cellId, kind, indexText, index, cell, artifacts, artifactList, text, offset, limit, page, nextOffset, done},
|
|
1208
1214
|
notebookId = TargetNotebookId[args];
|
|
1209
|
-
If[RequireReadPermission[notebookId] === $Canceled, Return[$Canceled]];
|
|
1210
|
-
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1211
|
-
record = NotebookRecord[notebookId];
|
|
1212
|
-
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1213
|
-
artifactId = Lookup[args, "artifactId", ""];
|
|
1214
|
-
If[!StringQ[artifactId] || StringLength[artifactId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "artifactId is required."|>]]];
|
|
1215
|
-
parts = StringSplit[artifactId, ":"];
|
|
1216
|
-
If[Length[parts] =!= 3, Return[Failure["BAD_REQUEST", <|"message" -> "artifactId must have the form cellId:type:index."|>]]];
|
|
1217
|
-
{cellId, kind, indexText} = parts;
|
|
1218
|
-
If[!MemberQ[{"output", "message"}, kind], Return[Failure["BAD_REQUEST", <|"message" -> "artifact type must be output or message."|>]]];
|
|
1219
|
-
If[!StringMatchQ[indexText, DigitCharacter..], Return[Failure["BAD_REQUEST", <|"message" -> "artifact index must be a non-negative integer."|>]]];
|
|
1220
|
-
index = ToExpression[indexText];
|
|
1221
|
-
offset = Lookup[args, "offset", 0];
|
|
1222
|
-
limit = Lookup[args, "limit", 65536];
|
|
1223
|
-
If[!IntegerQ[offset] || offset < 0, Return[Failure["BAD_REQUEST", <|"message" -> "offset must be a non-negative integer."|>]]];
|
|
1224
|
-
If[!IntegerQ[limit] || limit <= 0 || limit > $MaxCellPayloadBytes, Return[Failure["BAD_REQUEST", <|"message" -> "limit must be a positive integer up to 1 MiB."|>]]];
|
|
1225
|
-
cell = Lookup[Lookup[record, "cellMap", <||>], cellId, Missing["NotFound"]];
|
|
1226
|
-
If[MissingQ[cell], Return[Failure["BAD_REQUEST", <|"message" -> "Requested cell was not found."|>]]];
|
|
1227
|
-
artifacts = CellArtifactScan[cell, cellId, Lookup[record, "notebook", None]];
|
|
1228
|
-
artifactList = If[kind === "output", artifacts["outputs"], artifacts["messages"]];
|
|
1229
|
-
If[index >= Length[artifactList], Return[Failure["BAD_REQUEST", <|"message" -> "Requested artifact was not found."|>]]];
|
|
1230
|
-
text = artifactList[[index + 1]];
|
|
1231
|
-
page = Utf8SliceStringToBytes[text, offset, limit];
|
|
1232
|
-
nextOffset = page["nextOffset"];
|
|
1233
|
-
done = nextOffset >= page["originalByteLength"];
|
|
1234
|
-
<|
|
|
1235
|
-
"artifactId" -> artifactId,
|
|
1236
|
-
"offset" -> offset,
|
|
1237
|
-
"limit" -> limit,
|
|
1238
|
-
"data" -> page["value"],
|
|
1239
|
-
"nextOffset" -> nextOffset,
|
|
1240
|
-
"done" -> done,
|
|
1241
|
-
"byteLength" -> page["originalByteLength"]
|
|
1242
|
-
|>
|
|
1243
|
-
];
|
|
1244
|
-
|
|
1245
|
-
MakeCellExpression[content_String, style_String] := Module[{cellStyle = If[StringQ[style] && StringLength[style] > 0, style, "Input"]},
|
|
1215
|
+
If[RequireReadPermission[notebookId] === $Canceled, Return[$Canceled]];
|
|
1216
|
+
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1217
|
+
record = NotebookRecord[notebookId];
|
|
1218
|
+
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1219
|
+
artifactId = Lookup[args, "artifactId", ""];
|
|
1220
|
+
If[!StringQ[artifactId] || StringLength[artifactId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "artifactId is required."|>]]];
|
|
1221
|
+
parts = StringSplit[artifactId, ":"];
|
|
1222
|
+
If[Length[parts] =!= 3, Return[Failure["BAD_REQUEST", <|"message" -> "artifactId must have the form cellId:type:index."|>]]];
|
|
1223
|
+
{cellId, kind, indexText} = parts;
|
|
1224
|
+
If[!MemberQ[{"output", "message"}, kind], Return[Failure["BAD_REQUEST", <|"message" -> "artifact type must be output or message."|>]]];
|
|
1225
|
+
If[!StringMatchQ[indexText, DigitCharacter..], Return[Failure["BAD_REQUEST", <|"message" -> "artifact index must be a non-negative integer."|>]]];
|
|
1226
|
+
index = ToExpression[indexText];
|
|
1227
|
+
offset = Lookup[args, "offset", 0];
|
|
1228
|
+
limit = Lookup[args, "limit", 65536];
|
|
1229
|
+
If[!IntegerQ[offset] || offset < 0, Return[Failure["BAD_REQUEST", <|"message" -> "offset must be a non-negative integer."|>]]];
|
|
1230
|
+
If[!IntegerQ[limit] || limit <= 0 || limit > $MaxCellPayloadBytes, Return[Failure["BAD_REQUEST", <|"message" -> "limit must be a positive integer up to 1 MiB."|>]]];
|
|
1231
|
+
cell = Lookup[Lookup[record, "cellMap", <||>], cellId, Missing["NotFound"]];
|
|
1232
|
+
If[MissingQ[cell], Return[Failure["BAD_REQUEST", <|"message" -> "Requested cell was not found."|>]]];
|
|
1233
|
+
artifacts = CellArtifactScan[cell, cellId, Lookup[record, "notebook", None]];
|
|
1234
|
+
artifactList = If[kind === "output", artifacts["outputs"], artifacts["messages"]];
|
|
1235
|
+
If[index >= Length[artifactList], Return[Failure["BAD_REQUEST", <|"message" -> "Requested artifact was not found."|>]]];
|
|
1236
|
+
text = artifactList[[index + 1]];
|
|
1237
|
+
page = Utf8SliceStringToBytes[text, offset, limit];
|
|
1238
|
+
nextOffset = page["nextOffset"];
|
|
1239
|
+
done = nextOffset >= page["originalByteLength"];
|
|
1240
|
+
<|
|
|
1241
|
+
"artifactId" -> artifactId,
|
|
1242
|
+
"offset" -> offset,
|
|
1243
|
+
"limit" -> limit,
|
|
1244
|
+
"data" -> page["value"],
|
|
1245
|
+
"nextOffset" -> nextOffset,
|
|
1246
|
+
"done" -> done,
|
|
1247
|
+
"byteLength" -> page["originalByteLength"]
|
|
1248
|
+
|>
|
|
1249
|
+
];
|
|
1250
|
+
|
|
1251
|
+
MakeCellExpression[content_String, style_String] := Module[{cellStyle = If[StringQ[style] && StringLength[style] > 0, style, "Input"]},
|
|
1246
1252
|
If[cellStyle === "Input",
|
|
1247
1253
|
Cell[BoxData[content], "Input", CellTags -> {"AI-Generated"}],
|
|
1248
1254
|
Cell[content, cellStyle, CellTags -> {"AI-Generated"}]
|
|
@@ -1384,14 +1390,14 @@ RunCellRequest[args_Association] := Module[{notebookId, record, notebook, cellId
|
|
|
1384
1390
|
If[MissingQ[cell], Return[Failure["BAD_REQUEST", <|"message" -> "Requested cell was not found."|>]]];
|
|
1385
1391
|
ClearCellEvaluationComplete[notebook, cellId];
|
|
1386
1392
|
installedEpilog = InstallRunningCellEpilog[cell, cellId];
|
|
1387
|
-
If[MatchQ[installedEpilog, _Failure], Return[installedEpilog]];
|
|
1388
|
-
$LastRunStatusCellId = None;
|
|
1389
|
-
$LastRunStatusNotebookId = None;
|
|
1390
|
-
$LastRunStatus = None;
|
|
1391
|
-
$RunningStatus = "running";
|
|
1392
|
-
$AbortRequestedAt = None;
|
|
1393
|
-
$LastLateResult = None;
|
|
1394
|
-
$RunningRequestId = Lookup[args, "requestId", $CurrentRequestId];
|
|
1393
|
+
If[MatchQ[installedEpilog, _Failure], Return[installedEpilog]];
|
|
1394
|
+
$LastRunStatusCellId = None;
|
|
1395
|
+
$LastRunStatusNotebookId = None;
|
|
1396
|
+
$LastRunStatus = None;
|
|
1397
|
+
$RunningStatus = "running";
|
|
1398
|
+
$AbortRequestedAt = None;
|
|
1399
|
+
$LastLateResult = None;
|
|
1400
|
+
$RunningRequestId = Lookup[args, "requestId", $CurrentRequestId];
|
|
1395
1401
|
$RunningCellId = cellId;
|
|
1396
1402
|
$RunningNotebookId = notebookId;
|
|
1397
1403
|
$RunningNotebookObject = notebook;
|
|
@@ -1424,15 +1430,15 @@ AbortEvaluationRequest[args_Association] := Module[{notebookId, record, notebook
|
|
|
1424
1430
|
FinishRunningCell["finished"];
|
|
1425
1431
|
Return[<|"status" -> "finished", "cellId" -> runningCellId, "requestId" -> runningRequestId|>]
|
|
1426
1432
|
];
|
|
1427
|
-
If[wasRunning,
|
|
1428
|
-
$AbortRequestedAt = AbsoluteTime[];
|
|
1429
|
-
$RunningStatus = "abort_requested"
|
|
1430
|
-
];
|
|
1431
|
-
Quiet @ Check[FrontEndTokenExecute[notebook, "EvaluatorAbort"], Null];
|
|
1432
|
-
If[wasRunning,
|
|
1433
|
-
<|"status" -> "abort_requested", "cellId" -> runningCellId, "requestId" -> runningRequestId|>,
|
|
1434
|
-
<|"status" -> "idle"|>
|
|
1435
|
-
]
|
|
1433
|
+
If[wasRunning,
|
|
1434
|
+
$AbortRequestedAt = AbsoluteTime[];
|
|
1435
|
+
$RunningStatus = "abort_requested"
|
|
1436
|
+
];
|
|
1437
|
+
Quiet @ Check[FrontEndTokenExecute[notebook, "EvaluatorAbort"], Null];
|
|
1438
|
+
If[wasRunning,
|
|
1439
|
+
<|"status" -> "abort_requested", "cellId" -> runningCellId, "requestId" -> runningRequestId|>,
|
|
1440
|
+
<|"status" -> "idle"|>
|
|
1441
|
+
]
|
|
1436
1442
|
];
|
|
1437
1443
|
|
|
1438
1444
|
KillKernelRequest[args_Association] := Module[{notebookId, record, notebook, evaluatorName},
|
|
@@ -1467,9 +1473,42 @@ RestartKernelRequest[args_Association] := Module[{notebookId, record, notebook,
|
|
|
1467
1473
|
<|"status" -> "restarted", "notebookId" -> notebookId|>
|
|
1468
1474
|
];
|
|
1469
1475
|
|
|
1476
|
+
CreateNotebookRequest[args_Association] := Module[{title, nb, notebookId, payload, response},
|
|
1477
|
+
title = Lookup[args, "title", "Untitled"];
|
|
1478
|
+
If[!StringQ[title] || StringLength[StringTrim[title]] == 0, title = "Untitled"];
|
|
1479
|
+
nb = Quiet @ Check[CreateDocument[{}, WindowTitle -> title, Visible -> True], $Failed];
|
|
1480
|
+
If[Head[nb] =!= NotebookObject, Return[Failure["CREATE_FAILED", <|"message" -> "The FrontEnd failed to create the notebook."|>]]];
|
|
1481
|
+
payload = NotebookHeartbeatPayload[nb];
|
|
1482
|
+
response = Quiet @ Check[BridgePost["/notebooks/heartbeat", payload], $Failed];
|
|
1483
|
+
notebookId = If[AssociationQ[response],
|
|
1484
|
+
Lookup[Lookup[response, "notebook", <||>], "notebookId", Lookup[response, "notebookId", None]],
|
|
1485
|
+
None
|
|
1486
|
+
];
|
|
1487
|
+
<|"status" -> "created", "notebookId" -> notebookId, "title" -> title|>
|
|
1488
|
+
];
|
|
1489
|
+
|
|
1490
|
+
OpenNotebookRequest[args_Association] := Module[{path, nb, notebookId, payload, response},
|
|
1491
|
+
path = Lookup[args, "path", ""];
|
|
1492
|
+
If[!StringQ[path] || StringLength[StringTrim[path]] == 0,
|
|
1493
|
+
Return[Failure["BAD_REQUEST", <|"message" -> "path is required."|>]]
|
|
1494
|
+
];
|
|
1495
|
+
If[!FileExistsQ[path],
|
|
1496
|
+
Return[Failure["NOT_FOUND", <|"message" -> "File not found: " <> path|>]]
|
|
1497
|
+
];
|
|
1498
|
+
nb = Quiet @ Check[NotebookOpen[path, Visible -> True], $Failed];
|
|
1499
|
+
If[Head[nb] =!= NotebookObject, Return[Failure["OPEN_FAILED", <|"message" -> "The FrontEnd failed to open the notebook."|>]]];
|
|
1500
|
+
payload = NotebookHeartbeatPayload[nb];
|
|
1501
|
+
response = Quiet @ Check[BridgePost["/notebooks/heartbeat", payload], $Failed];
|
|
1502
|
+
notebookId = If[AssociationQ[response],
|
|
1503
|
+
Lookup[Lookup[response, "notebook", <||>], "notebookId", Lookup[response, "notebookId", None]],
|
|
1504
|
+
None
|
|
1505
|
+
];
|
|
1506
|
+
<|"status" -> "opened", "notebookId" -> notebookId, "path" -> path|>
|
|
1507
|
+
];
|
|
1508
|
+
|
|
1470
1509
|
SaveNotebookRequest[args_Association] := Module[{notebookId, record, notebook},
|
|
1471
1510
|
notebookId = TargetNotebookId[args];
|
|
1472
|
-
If[Not @ ConfirmAction["SaveNotebook", "AI requests saving the notebook. Allow?", notebookId], Return[$Canceled]];
|
|
1511
|
+
If[Not @ ConfirmAction["SaveNotebook", "AI requests saving the notebook. Allow?", notebookId], Return[$Canceled]];
|
|
1473
1512
|
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1474
1513
|
record = NotebookRecord[notebookId];
|
|
1475
1514
|
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
@@ -1839,13 +1878,15 @@ ExecuteRequest[request_Association] := Module[{requestId, tool, args, result},
|
|
|
1839
1878
|
"mma_insert_cell", InsertCellRequest[args],
|
|
1840
1879
|
"mma_modify_cell", ModifyCellRequest[args],
|
|
1841
1880
|
"mma_delete_cell", DeleteCellRequest[args],
|
|
1842
|
-
"mma_run_cell", RunCellRequest[args],
|
|
1881
|
+
"mma_run_cell", RunCellRequest[args],
|
|
1843
1882
|
"mma_abort_evaluation", AbortEvaluationRequest[args],
|
|
1844
1883
|
"mma_kill_kernel", KillKernelRequest[args],
|
|
1845
|
-
"mma_restart_kernel", RestartKernelRequest[args],
|
|
1846
|
-
"
|
|
1847
|
-
"
|
|
1848
|
-
"
|
|
1884
|
+
"mma_restart_kernel", RestartKernelRequest[args],
|
|
1885
|
+
"mma_create_notebook", CreateNotebookRequest[args],
|
|
1886
|
+
"mma_open_notebook", OpenNotebookRequest[args],
|
|
1887
|
+
"mma_get_cell_output", GetCellOutputById[args],
|
|
1888
|
+
"mma_read_artifact", ReadArtifactById[args],
|
|
1889
|
+
"mma_save_notebook", SaveNotebookRequest[args],
|
|
1849
1890
|
"mma_select_notebook", SelectNotebookRequest[args],
|
|
1850
1891
|
"mma_symbol_lookup", SymbolLookup[Lookup[args, "query", ""]],
|
|
1851
1892
|
_, Failure["BAD_REQUEST", <|"Message" -> StringTemplate["Unknown tool ``."][tool]|>]
|