@aliceshimada/mica 1.1.1 → 1.2.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 +14 -0
- package/dist/src/backend/backendQueue.js +4 -1
- package/dist/src/backend/protocol.js +1 -1
- package/dist/src/bun/httpServer.js +2 -2
- package/dist/src/bun/index.js +6 -1
- package/dist/src/mcp/backendTools.js +34 -2
- package/dist/src/mcp/prompts.js +3 -1
- package/dist/src/mcp/toolSchemas.js +7 -1
- package/package.json +1 -1
- package/paclet/Kernel/MMAAgentBridge.wl +384 -334
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.1 - 2026-06-17
|
|
4
|
+
|
|
5
|
+
- Add watchdog on agent tick loop: force-reset `$HiddenAgentInProgress` if stuck > 120s.
|
|
6
|
+
- Add periodic backend sweep (every 10s) for liveness and timed-out requests.
|
|
7
|
+
- Fix `markTimedOut` to use `claimedAt` for running requests.
|
|
8
|
+
- Remove `runCell` timeout upper bound; default to no timeout.
|
|
9
|
+
|
|
10
|
+
## 1.2.0 - 2026-06-16
|
|
11
|
+
|
|
12
|
+
- Add `mma_create_notebook` tool: create a new blank notebook in the Wolfram FrontEnd.
|
|
13
|
+
- Add `mma_open_notebook` tool: open an existing notebook file (.nb) from disk.
|
|
14
|
+
- Add `CreateNotebook` and `OpenNotebook` permissions (default false).
|
|
15
|
+
- Fix agent-level tools when no live notebook exists for routing.
|
|
16
|
+
|
|
3
17
|
## 1.1.1 - 2026-06-16
|
|
4
18
|
|
|
5
19
|
- Fix `RestartKernelRequest`: kill kernel via `Quit[]` before restarting.
|
|
@@ -81,7 +81,10 @@ export class BackendQueue {
|
|
|
81
81
|
for (const request of this.requests.values()) {
|
|
82
82
|
if (request.status !== "queued" && request.status !== "running")
|
|
83
83
|
continue;
|
|
84
|
-
|
|
84
|
+
const elapsed = request.status === "running" && request.claimedAt
|
|
85
|
+
? now - request.claimedAt
|
|
86
|
+
: now - request.createdAt;
|
|
87
|
+
if (elapsed < request.timeoutMs)
|
|
85
88
|
continue;
|
|
86
89
|
const updated = this.cloneRequest({ ...request, status: "timed_out" });
|
|
87
90
|
this.requests.set(request.requestId, updated);
|
|
@@ -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.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,
|
|
@@ -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.1";
|
|
12
12
|
export async function startBunRuntime(deps = {}) {
|
|
13
13
|
const config = deps.runtimeConfig ?? loadRuntimeConfig();
|
|
14
14
|
const bridgeOnly = deps.bridgeOnly ?? config.bridgeOnly;
|
|
@@ -65,6 +65,11 @@ export async function startBunRuntime(deps = {}) {
|
|
|
65
65
|
console.error("Bun MCP mode enabled; connecting stdio transport.");
|
|
66
66
|
await server.connect(createTransport());
|
|
67
67
|
}
|
|
68
|
+
const sweepInterval = setInterval(() => {
|
|
69
|
+
state.sweepLiveness();
|
|
70
|
+
state.queue.markTimedOut(Date.now());
|
|
71
|
+
}, 10_000);
|
|
72
|
+
sweepInterval.unref();
|
|
68
73
|
}
|
|
69
74
|
catch (error) {
|
|
70
75
|
await stop();
|
|
@@ -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) {
|
|
@@ -128,7 +128,7 @@ const queuedNotebookTools = [
|
|
|
128
128
|
schema: runCellSchema.shape,
|
|
129
129
|
permission: "RunCell",
|
|
130
130
|
timeoutMs: (args) => {
|
|
131
|
-
const timeoutSec = typeof args.timeoutSec === "number" ? args.timeoutSec :
|
|
131
|
+
const timeoutSec = typeof args.timeoutSec === "number" ? args.timeoutSec : 86_400;
|
|
132
132
|
return timeoutSec * 1000;
|
|
133
133
|
},
|
|
134
134
|
requiresExplicitTarget: true,
|
|
@@ -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.1") {
|
|
39
41
|
return new McpServer({ name, version }, { instructions: MICA_AGENT_INSTRUCTIONS });
|
|
40
42
|
}
|
|
41
43
|
export function registerMicaPrompts(server) {
|
|
@@ -33,7 +33,7 @@ export const deleteCellSchema = z.object({
|
|
|
33
33
|
export const runCellSchema = z.object({
|
|
34
34
|
...notebookSelectorFields,
|
|
35
35
|
cellId: z.string().min(1),
|
|
36
|
-
timeoutSec: z.number().int().positive().
|
|
36
|
+
timeoutSec: z.number().int().positive().optional()
|
|
37
37
|
}).strict();
|
|
38
38
|
export const abortEvaluationSchema = z.object({
|
|
39
39
|
...notebookSelectorFields
|
|
@@ -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"}]
|
|
@@ -1370,7 +1376,7 @@ DeleteCellRequest[args_Association] := Module[{notebookId, record, notebook, cel
|
|
|
1370
1376
|
<|"status" -> "deleted", "cellId" -> cellId, "deletedArtifactCount" -> Length[artifacts]|>
|
|
1371
1377
|
];
|
|
1372
1378
|
|
|
1373
|
-
RunCellRequest[args_Association] := Module[{notebookId, record, notebook, cellId, cell, timeoutSec = Lookup[args, "timeoutSec",
|
|
1379
|
+
RunCellRequest[args_Association] := Module[{notebookId, record, notebook, cellId, cell, timeoutSec = Lookup[args, "timeoutSec", Infinity], installedEpilog, evaluateResult},
|
|
1374
1380
|
notebookId = TargetNotebookId[args];
|
|
1375
1381
|
If[Not @ ConfirmAction["RunCell", "AI requests running 1 cell. Allow?", notebookId], Return[$Canceled]];
|
|
1376
1382
|
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
@@ -1384,20 +1390,19 @@ 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;
|
|
1398
1404
|
$RunningStartedAt = AbsoluteTime[];
|
|
1399
|
-
If[
|
|
1400
|
-
$RunningTimeoutAt = AbsoluteTime[] + timeoutSec;
|
|
1405
|
+
$RunningTimeoutAt = If[NumericQ[timeoutSec] && timeoutSec < Infinity, AbsoluteTime[] + timeoutSec, Infinity];
|
|
1401
1406
|
SelectionMove[cell, All, Cell];
|
|
1402
1407
|
(* RunCell returns immediately; Palette-local cancellation can still abort the running evaluation. *)
|
|
1403
1408
|
evaluateResult = Quiet @ Check[FrontEndTokenExecute[notebook, "EvaluateCells"], $Failed];
|
|
@@ -1424,15 +1429,15 @@ AbortEvaluationRequest[args_Association] := Module[{notebookId, record, notebook
|
|
|
1424
1429
|
FinishRunningCell["finished"];
|
|
1425
1430
|
Return[<|"status" -> "finished", "cellId" -> runningCellId, "requestId" -> runningRequestId|>]
|
|
1426
1431
|
];
|
|
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
|
-
]
|
|
1432
|
+
If[wasRunning,
|
|
1433
|
+
$AbortRequestedAt = AbsoluteTime[];
|
|
1434
|
+
$RunningStatus = "abort_requested"
|
|
1435
|
+
];
|
|
1436
|
+
Quiet @ Check[FrontEndTokenExecute[notebook, "EvaluatorAbort"], Null];
|
|
1437
|
+
If[wasRunning,
|
|
1438
|
+
<|"status" -> "abort_requested", "cellId" -> runningCellId, "requestId" -> runningRequestId|>,
|
|
1439
|
+
<|"status" -> "idle"|>
|
|
1440
|
+
]
|
|
1436
1441
|
];
|
|
1437
1442
|
|
|
1438
1443
|
KillKernelRequest[args_Association] := Module[{notebookId, record, notebook, evaluatorName},
|
|
@@ -1467,9 +1472,42 @@ RestartKernelRequest[args_Association] := Module[{notebookId, record, notebook,
|
|
|
1467
1472
|
<|"status" -> "restarted", "notebookId" -> notebookId|>
|
|
1468
1473
|
];
|
|
1469
1474
|
|
|
1475
|
+
CreateNotebookRequest[args_Association] := Module[{title, nb, notebookId, payload, response},
|
|
1476
|
+
title = Lookup[args, "title", "Untitled"];
|
|
1477
|
+
If[!StringQ[title] || StringLength[StringTrim[title]] == 0, title = "Untitled"];
|
|
1478
|
+
nb = Quiet @ Check[CreateDocument[{}, WindowTitle -> title, Visible -> True], $Failed];
|
|
1479
|
+
If[Head[nb] =!= NotebookObject, Return[Failure["CREATE_FAILED", <|"message" -> "The FrontEnd failed to create the notebook."|>]]];
|
|
1480
|
+
payload = NotebookHeartbeatPayload[nb];
|
|
1481
|
+
response = Quiet @ Check[BridgePost["/notebooks/heartbeat", payload], $Failed];
|
|
1482
|
+
notebookId = If[AssociationQ[response],
|
|
1483
|
+
Lookup[Lookup[response, "notebook", <||>], "notebookId", Lookup[response, "notebookId", None]],
|
|
1484
|
+
None
|
|
1485
|
+
];
|
|
1486
|
+
<|"status" -> "created", "notebookId" -> notebookId, "title" -> title|>
|
|
1487
|
+
];
|
|
1488
|
+
|
|
1489
|
+
OpenNotebookRequest[args_Association] := Module[{path, nb, notebookId, payload, response},
|
|
1490
|
+
path = Lookup[args, "path", ""];
|
|
1491
|
+
If[!StringQ[path] || StringLength[StringTrim[path]] == 0,
|
|
1492
|
+
Return[Failure["BAD_REQUEST", <|"message" -> "path is required."|>]]
|
|
1493
|
+
];
|
|
1494
|
+
If[!FileExistsQ[path],
|
|
1495
|
+
Return[Failure["NOT_FOUND", <|"message" -> "File not found: " <> path|>]]
|
|
1496
|
+
];
|
|
1497
|
+
nb = Quiet @ Check[NotebookOpen[path, Visible -> True], $Failed];
|
|
1498
|
+
If[Head[nb] =!= NotebookObject, Return[Failure["OPEN_FAILED", <|"message" -> "The FrontEnd failed to open the notebook."|>]]];
|
|
1499
|
+
payload = NotebookHeartbeatPayload[nb];
|
|
1500
|
+
response = Quiet @ Check[BridgePost["/notebooks/heartbeat", payload], $Failed];
|
|
1501
|
+
notebookId = If[AssociationQ[response],
|
|
1502
|
+
Lookup[Lookup[response, "notebook", <||>], "notebookId", Lookup[response, "notebookId", None]],
|
|
1503
|
+
None
|
|
1504
|
+
];
|
|
1505
|
+
<|"status" -> "opened", "notebookId" -> notebookId, "path" -> path|>
|
|
1506
|
+
];
|
|
1507
|
+
|
|
1470
1508
|
SaveNotebookRequest[args_Association] := Module[{notebookId, record, notebook},
|
|
1471
1509
|
notebookId = TargetNotebookId[args];
|
|
1472
|
-
If[Not @ ConfirmAction["SaveNotebook", "AI requests saving the notebook. Allow?", notebookId], Return[$Canceled]];
|
|
1510
|
+
If[Not @ ConfirmAction["SaveNotebook", "AI requests saving the notebook. Allow?", notebookId], Return[$Canceled]];
|
|
1473
1511
|
If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
1474
1512
|
record = NotebookRecord[notebookId];
|
|
1475
1513
|
If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
|
|
@@ -1661,7 +1699,15 @@ ExecuteAgentRequest[request_Association] := Module[{args = Lookup[request, "argu
|
|
|
1661
1699
|
];
|
|
1662
1700
|
|
|
1663
1701
|
SafeHiddenAgentTick[] := Module[{payload, request},
|
|
1664
|
-
If[TrueQ[$HiddenAgentInProgress],
|
|
1702
|
+
If[TrueQ[$HiddenAgentInProgress],
|
|
1703
|
+
If[NumberQ[$HiddenAgentStartedAt] && AbsoluteTime[] - $HiddenAgentStartedAt > 120,
|
|
1704
|
+
$HiddenAgentInProgress = False;
|
|
1705
|
+
$HiddenAgentStartedAt = None;
|
|
1706
|
+
$HiddenAgentStartedAt = None,
|
|
1707
|
+
Return[$LastResultStatus]
|
|
1708
|
+
]
|
|
1709
|
+
];
|
|
1710
|
+
$HiddenAgentStartedAt = AbsoluteTime[];
|
|
1665
1711
|
Internal`WithLocalSettings[
|
|
1666
1712
|
$HiddenAgentInProgress = True,
|
|
1667
1713
|
(
|
|
@@ -1686,7 +1732,9 @@ SafeHiddenAgentTick[] := Module[{payload, request},
|
|
|
1686
1732
|
];
|
|
1687
1733
|
Null
|
|
1688
1734
|
),
|
|
1689
|
-
|
|
1735
|
+
$HiddenAgentInProgress = False;
|
|
1736
|
+
$HiddenAgentStartedAt = None;
|
|
1737
|
+
$HiddenAgentStartedAt = None
|
|
1690
1738
|
]
|
|
1691
1739
|
];
|
|
1692
1740
|
|
|
@@ -1839,13 +1887,15 @@ ExecuteRequest[request_Association] := Module[{requestId, tool, args, result},
|
|
|
1839
1887
|
"mma_insert_cell", InsertCellRequest[args],
|
|
1840
1888
|
"mma_modify_cell", ModifyCellRequest[args],
|
|
1841
1889
|
"mma_delete_cell", DeleteCellRequest[args],
|
|
1842
|
-
"mma_run_cell", RunCellRequest[args],
|
|
1890
|
+
"mma_run_cell", RunCellRequest[args],
|
|
1843
1891
|
"mma_abort_evaluation", AbortEvaluationRequest[args],
|
|
1844
1892
|
"mma_kill_kernel", KillKernelRequest[args],
|
|
1845
|
-
"mma_restart_kernel", RestartKernelRequest[args],
|
|
1846
|
-
"
|
|
1847
|
-
"
|
|
1848
|
-
"
|
|
1893
|
+
"mma_restart_kernel", RestartKernelRequest[args],
|
|
1894
|
+
"mma_create_notebook", CreateNotebookRequest[args],
|
|
1895
|
+
"mma_open_notebook", OpenNotebookRequest[args],
|
|
1896
|
+
"mma_get_cell_output", GetCellOutputById[args],
|
|
1897
|
+
"mma_read_artifact", ReadArtifactById[args],
|
|
1898
|
+
"mma_save_notebook", SaveNotebookRequest[args],
|
|
1849
1899
|
"mma_select_notebook", SelectNotebookRequest[args],
|
|
1850
1900
|
"mma_symbol_lookup", SymbolLookup[Lookup[args, "query", ""]],
|
|
1851
1901
|
_, Failure["BAD_REQUEST", <|"Message" -> StringTemplate["Unknown tool ``."][tool]|>]
|