@axiom-lattice/core 6.4.0 → 6.4.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/dist/index.d.mts +38 -3
- package/dist/index.d.ts +38 -3
- package/dist/index.js +225 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +224 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1929,6 +1929,7 @@ __export(index_exports, {
|
|
|
1929
1929
|
globSearchFiles: () => globSearchFiles,
|
|
1930
1930
|
grepMatchesFromFiles: () => grepMatchesFromFiles,
|
|
1931
1931
|
grepSearchFiles: () => grepSearchFiles,
|
|
1932
|
+
handlerResultText: () => handlerResultText,
|
|
1932
1933
|
hasChunkBuffer: () => hasChunkBuffer,
|
|
1933
1934
|
hasDelegatedWorkerToken: () => hasDelegatedWorkerToken,
|
|
1934
1935
|
hasRuntimeCapabilityPolicy: () => hasRuntimeCapabilityPolicy,
|
|
@@ -15322,6 +15323,12 @@ function capResult(result, cap) {
|
|
|
15322
15323
|
const serialized = typeof result === "string" ? result : JSON.stringify(result ?? null);
|
|
15323
15324
|
return Buffer.byteLength(serialized, "utf8") <= cap ? result : truncate(serialized, cap);
|
|
15324
15325
|
}
|
|
15326
|
+
function handlerResultText(result) {
|
|
15327
|
+
const value = result.result;
|
|
15328
|
+
if (typeof value === "string") return value;
|
|
15329
|
+
if (value === void 0) return (result.stdout ?? "").trim();
|
|
15330
|
+
return JSON.stringify(value);
|
|
15331
|
+
}
|
|
15325
15332
|
function errorFromStdout(stdout) {
|
|
15326
15333
|
let text = stdout;
|
|
15327
15334
|
try {
|
|
@@ -16253,7 +16260,7 @@ function compileSandboxPlugin(args) {
|
|
|
16253
16260
|
maxResultBytes: t.maxResultBytes
|
|
16254
16261
|
});
|
|
16255
16262
|
if (!result.ok) return result.error ?? "sandbox plugin execution failed";
|
|
16256
|
-
const text =
|
|
16263
|
+
const text = handlerResultText(result);
|
|
16257
16264
|
const ref = t.ui ? { kind: "plugin", pluginType: manifest.type, resource: t.ui.resource, displayMode: t.ui.displayMode ?? "inline" } : null;
|
|
16258
16265
|
return ref ? appendUiFence(text, ref) : text;
|
|
16259
16266
|
},
|
|
@@ -16649,7 +16656,7 @@ var TenantPluginCatalog = class {
|
|
|
16649
16656
|
// src/sandbox_plugin/sandboxPluginRuntime.ts
|
|
16650
16657
|
var catalog = null;
|
|
16651
16658
|
function buildResidentRouting() {
|
|
16652
|
-
const baseUrl = process.env.
|
|
16659
|
+
const baseUrl = process.env.MICROSANDBOX_SERVICE_BASE_URL;
|
|
16653
16660
|
if (!baseUrl) return void 0;
|
|
16654
16661
|
return {
|
|
16655
16662
|
getDeployment: async (tenantId2, pluginType) => await getPluginDeploymentService()?.getDeployment(tenantId2, pluginType) ?? void 0,
|
|
@@ -35469,7 +35476,9 @@ var MicrosandboxServiceClient = class {
|
|
|
35469
35476
|
}
|
|
35470
35477
|
} catch {
|
|
35471
35478
|
}
|
|
35472
|
-
|
|
35479
|
+
const error = new Error(`Microsandbox service request failed: ${detail}`);
|
|
35480
|
+
error.statusCode = response.status;
|
|
35481
|
+
throw error;
|
|
35473
35482
|
}
|
|
35474
35483
|
const payload = await response.json();
|
|
35475
35484
|
if (payload?.success === false) {
|
|
@@ -38918,6 +38927,7 @@ var OutboxPoller = class {
|
|
|
38918
38927
|
};
|
|
38919
38928
|
|
|
38920
38929
|
// src/services/ProjectMutationMutex.ts
|
|
38930
|
+
var import_node_async_hooks = require("async_hooks");
|
|
38921
38931
|
var import_protocols28 = require("@axiom-lattice/protocols");
|
|
38922
38932
|
|
|
38923
38933
|
// src/services/coordinationLease.ts
|
|
@@ -38987,16 +38997,24 @@ var ProjectMutationMutex = class {
|
|
|
38987
38997
|
constructor() {
|
|
38988
38998
|
this.queues = /* @__PURE__ */ new Map();
|
|
38989
38999
|
this.active = /* @__PURE__ */ new Set();
|
|
39000
|
+
/** Project keys already held by the current async context (reentrancy). */
|
|
39001
|
+
this.held = new import_node_async_hooks.AsyncLocalStorage();
|
|
38990
39002
|
}
|
|
38991
39003
|
/** Runs one mutation after all earlier mutations for the same Tenant and Project. */
|
|
38992
39004
|
async runExclusive(scope, operation, signal) {
|
|
38993
39005
|
const key5 = projectKey(scope);
|
|
39006
|
+
const inherited = this.held.getStore();
|
|
39007
|
+
if (inherited?.has(key5)) return operation();
|
|
38994
39008
|
const leaseKey = `project-mutation:${encodeURIComponent(scope.tenantId)}:${encodeURIComponent(scope.projectId)}`;
|
|
38995
39009
|
await this.acquire(key5, signal);
|
|
39010
|
+
const held = new Set(inherited);
|
|
39011
|
+
held.add(key5);
|
|
38996
39012
|
try {
|
|
38997
|
-
|
|
38998
|
-
|
|
38999
|
-
|
|
39013
|
+
return await this.held.run(held, async () => {
|
|
39014
|
+
const store = getCoordinationLeaseStore();
|
|
39015
|
+
if (!store) return await operation();
|
|
39016
|
+
return await withLease(store, leaseKey, getReplicaId(), operation, MUTATION_LEASE_OPTIONS);
|
|
39017
|
+
});
|
|
39000
39018
|
} finally {
|
|
39001
39019
|
this.release(key5);
|
|
39002
39020
|
}
|
|
@@ -39070,6 +39088,7 @@ var INPUT_KEYS = [
|
|
|
39070
39088
|
];
|
|
39071
39089
|
var REPLY_INPUT_KEYS = ["tenantId", "membershipId", "text", "sourceRoomMessageId", "inputMessageId", "sourceId", "idempotencyKey"];
|
|
39072
39090
|
var INTERRUPT_INPUT_KEYS = ["tenantId", "membershipId", "text", "sourceRoomMessageId", "inputMessageId"];
|
|
39091
|
+
var CORRELATED_INTERRUPT_INPUT_KEYS = ["tenantId", "membershipId", "correlationKey", "text"];
|
|
39073
39092
|
var RoomAgentMessageService = class {
|
|
39074
39093
|
/**
|
|
39075
39094
|
* Creates a room writer over the durable membership and message stores.
|
|
@@ -39117,6 +39136,50 @@ var RoomAgentMessageService = class {
|
|
|
39117
39136
|
idempotencyKey: `room-interrupt:${snapshot.sourceRoomMessageId}:${snapshot.membershipId}:${sourceId}`
|
|
39118
39137
|
});
|
|
39119
39138
|
}
|
|
39139
|
+
/**
|
|
39140
|
+
* Persists a correlated HITL interrupt that has no human source message.
|
|
39141
|
+
*
|
|
39142
|
+
* @param input Explicit tenant, active membership, caller-supplied correlation key, and text.
|
|
39143
|
+
* @returns A deep clone of the canonical idempotent room message.
|
|
39144
|
+
* @remarks Business-agnostic: the caller owns the correlation key. Scope is
|
|
39145
|
+
* validated against the durable membership; no source message is required.
|
|
39146
|
+
*/
|
|
39147
|
+
postCorrelatedInterrupt(input) {
|
|
39148
|
+
const snapshot = exactStringRecord(input, CORRELATED_INTERRUPT_INPUT_KEYS, "Invalid project room interrupt input");
|
|
39149
|
+
return this.persistScoped({
|
|
39150
|
+
tenantId: snapshot.tenantId,
|
|
39151
|
+
membershipId: snapshot.membershipId,
|
|
39152
|
+
text: snapshot.text,
|
|
39153
|
+
sourceId: `interrupt:${snapshot.correlationKey}`,
|
|
39154
|
+
idempotencyKey: `room-interrupt:${snapshot.membershipId}:${snapshot.correlationKey}`
|
|
39155
|
+
});
|
|
39156
|
+
}
|
|
39157
|
+
async persistScoped(input) {
|
|
39158
|
+
const text = input.text.trim();
|
|
39159
|
+
if (text.length < 1 || text.length > 2e4) throw new Error("Invalid project room message text");
|
|
39160
|
+
const member = snapshotMembership(await this.deps.memberships.findById(input.tenantId, input.membershipId));
|
|
39161
|
+
if (!member || member.id !== input.membershipId || member.status !== "active" || member.tenantId !== input.tenantId) {
|
|
39162
|
+
throw new Error("Invalid project room membership");
|
|
39163
|
+
}
|
|
39164
|
+
const candidate = {
|
|
39165
|
+
id: (this.deps.idFactory ?? import_crypto8.randomUUID)(),
|
|
39166
|
+
tenantId: member.tenantId,
|
|
39167
|
+
workspaceId: member.workspaceId,
|
|
39168
|
+
projectId: member.projectId,
|
|
39169
|
+
roomId: member.roomId,
|
|
39170
|
+
author: { type: "bot", membershipId: member.id, assistantId: member.assistantId },
|
|
39171
|
+
content: { type: "text", text },
|
|
39172
|
+
mentions: [],
|
|
39173
|
+
source: "agent",
|
|
39174
|
+
sourceId: input.sourceId,
|
|
39175
|
+
idempotencyKey: input.idempotencyKey
|
|
39176
|
+
};
|
|
39177
|
+
const canonical = snapshotCanonicalScoped(await this.deps.messages.createIdempotent(candidate), candidate);
|
|
39178
|
+
if (!canonical) throw new Error("Invalid canonical project room message");
|
|
39179
|
+
const result = structuredClone(canonical);
|
|
39180
|
+
this.notifyCommitted(result);
|
|
39181
|
+
return result;
|
|
39182
|
+
}
|
|
39120
39183
|
async persistDerived(input) {
|
|
39121
39184
|
const source = snapshotSource(await this.deps.messages.findById(input.tenantId, input.sourceRoomMessageId));
|
|
39122
39185
|
if (!source || source.id !== input.sourceRoomMessageId || source.tenantId !== input.tenantId) throw new Error("Invalid project room message");
|
|
@@ -39272,6 +39335,27 @@ function snapshotCanonical(value, expected) {
|
|
|
39272
39335
|
createdAt
|
|
39273
39336
|
};
|
|
39274
39337
|
}
|
|
39338
|
+
function snapshotCanonicalScoped(value, expected) {
|
|
39339
|
+
const values = exactValues(value, ["id", "tenantId", "workspaceId", "projectId", "roomId", "author", "content", "mentions", "source", "sourceId", "idempotencyKey", "createdAt"], ["replyToMessageId"]);
|
|
39340
|
+
const author = exactValues(values?.author, ["type", "membershipId", "assistantId"]);
|
|
39341
|
+
const content = exactValues(values?.content, ["type", "text"]);
|
|
39342
|
+
const createdAt = date(values?.createdAt);
|
|
39343
|
+
if (!values || !createdAt || typeof values.id !== "string" || values.id.length === 0 || (0, import_protocols29.snapshotExactArray)(values.mentions)?.length !== 0 || author?.type !== "bot" || expected.author.type !== "bot" || author.membershipId !== expected.author.membershipId || author.assistantId !== expected.author.assistantId || content?.type !== "text" || content.text !== expected.content.text || values.tenantId !== expected.tenantId || values.workspaceId !== expected.workspaceId || values.projectId !== expected.projectId || values.roomId !== expected.roomId || values.source !== "agent" || values.sourceId !== expected.sourceId || values.idempotencyKey !== expected.idempotencyKey) return void 0;
|
|
39344
|
+
return {
|
|
39345
|
+
id: values.id,
|
|
39346
|
+
tenantId: expected.tenantId,
|
|
39347
|
+
workspaceId: expected.workspaceId,
|
|
39348
|
+
projectId: expected.projectId,
|
|
39349
|
+
roomId: expected.roomId,
|
|
39350
|
+
author: { type: "bot", membershipId: expected.author.membershipId, assistantId: expected.author.assistantId },
|
|
39351
|
+
content: { type: "text", text: expected.content.text },
|
|
39352
|
+
mentions: [],
|
|
39353
|
+
source: "agent",
|
|
39354
|
+
sourceId: expected.sourceId,
|
|
39355
|
+
idempotencyKey: expected.idempotencyKey,
|
|
39356
|
+
createdAt
|
|
39357
|
+
};
|
|
39358
|
+
}
|
|
39275
39359
|
|
|
39276
39360
|
// src/services/ProjectTaskOwnerPolicy.ts
|
|
39277
39361
|
var import_protocols30 = require("@axiom-lattice/protocols");
|
|
@@ -49141,6 +49225,18 @@ On top of that, add any of three combinable classes:
|
|
|
49141
49225
|
## Combinations
|
|
49142
49226
|
A plugin is the SET of classes it needs, e.g. \`connection + tools + skills\` or \`UI + connection\`.
|
|
49143
49227
|
|
|
49228
|
+
## Scenarios \u2192 skills
|
|
49229
|
+
| Scenario | Read |
|
|
49230
|
+
|---|---|
|
|
49231
|
+
| Tool that returns/transforms data | \`sandbox-plugins-manifest\` + \`sandbox-plugins-handlers\` |
|
|
49232
|
+
| Skills-only (procedural knowledge, no tools) | \`sandbox-plugins-authoring\` (skills) |
|
|
49233
|
+
| Tool needs credentials / resource discovery | \`sandbox-plugins-connection\` |
|
|
49234
|
+
| Tool shows an interactive panel | \`sandbox-plugins-ui\` |
|
|
49235
|
+
| Tool orchestrates platform tools/agents | \`sandbox-plugins-consume-open\` |
|
|
49236
|
+
| Tool callable by external MCP clients | \`sandbox-plugins-expose\` |
|
|
49237
|
+
| Run as a long-lived resident service | \`sandbox-plugins-deploy\` |
|
|
49238
|
+
| Validate / test / debug | \`sandbox-plugins-testing\` |
|
|
49239
|
+
|
|
49144
49240
|
## Agents (not a supported authoring path)
|
|
49145
49241
|
The manifest schema accepts an \`agents\` block and tenant plugin agents are
|
|
49146
49242
|
registered, but building agents this way is not supported or designed for tenant
|
|
@@ -49351,10 +49447,38 @@ lifecycle handshake:
|
|
|
49351
49447
|
- \`ui/notifications/tool-result\` \u2192 a \`CallToolResult\` \`{ content: [{ type: "text", text }] }\` (the tool output text, with any \`mcp_app\` fence stripped).
|
|
49352
49448
|
|
|
49353
49449
|
Register the \`message\` listener **before** \`ui/initialize\` so nothing is missed.
|
|
49354
|
-
|
|
49355
|
-
|
|
49356
|
-
|
|
49357
|
-
|
|
49450
|
+
Read the payload from \`params.content[0].text\` \u2014 the notification is a
|
|
49451
|
+
\`CallToolResult\`, **not** your handler payload at the top level. The result
|
|
49452
|
+
carries **TEXT content only** \u2014 no \`structuredContent\` or \`artifact\`; parse the
|
|
49453
|
+
JSON yourself if the tool emitted JSON (it is the handler's \`result\`, so the
|
|
49454
|
+
handler must wrap it under \`result\`). To re-query or refresh, call \`tools/call\`
|
|
49455
|
+
from the View and render its response (the host does not re-push \`tool-result\`
|
|
49456
|
+
for your own calls).
|
|
49457
|
+
|
|
49458
|
+
When you call \`tools/call\` from the View, the JSON-RPC response \`result\` is the
|
|
49459
|
+
handler's own \`result\` value **directly** \u2014 it is **not wrapped in a**
|
|
49460
|
+
\`CallToolResult\` \`{ content: [...] }\` (that wrapper is only used for the host's
|
|
49461
|
+
\`tool-result\` push). Parse it as your tool's data.
|
|
49462
|
+
|
|
49463
|
+
## Push vs re-execute (key for debugging)
|
|
49464
|
+
The host pushes \`tool-result\` from the tool result **recorded when the agent ran** \u2014
|
|
49465
|
+
it does **not** re-execute the handler. The push therefore always reflects that
|
|
49466
|
+
run's output (and the handler's \`result\` at that time). A \`tools/call\` from the
|
|
49467
|
+
View, by contrast, **re-executes** the handler live every time.
|
|
49468
|
+
|
|
49469
|
+
So if the push looks stale/wrong but a fresh \`tools/call\` returns the right data,
|
|
49470
|
+
the recorded run predates your change \u2014 re-run the tool (a new agent call) to get
|
|
49471
|
+
a fresh push. Do not assume the push re-reads the handler.
|
|
49472
|
+
|
|
49473
|
+
## Self-check / debug
|
|
49474
|
+
- Log the raw frame: \`console.log(JSON.stringify(ev.data))\` in the \`message\`
|
|
49475
|
+
listener, and open the browser console inside the sandboxed frame.
|
|
49476
|
+
- If the pushed text looks like the raw handler envelope (e.g. it starts with
|
|
49477
|
+
\`{"ok":true,...\`), the handler omitted the \`result\` wrapper \u2014 the host falls
|
|
49478
|
+
back to the raw stdout instead of your payload. Fix the handler (see
|
|
49479
|
+
\`sandbox-plugins-handlers\`).
|
|
49480
|
+
- The push only happens after \`ui/initialize\` **and**
|
|
49481
|
+
\`ui/notifications/initialized\`; a listener registered later misses it.
|
|
49358
49482
|
|
|
49359
49483
|
\`templates/ui.html\` implements all of this in vanilla JS and renders both payloads
|
|
49360
49484
|
into a \`#data\` element.
|
|
@@ -49474,6 +49598,76 @@ Templates: see \`sandbox-plugins-manifest\` for the exact \`tools[]\` fields; th
|
|
|
49474
49598
|
\`templates/plugin.json\` already includes \`"expose": true\` (the tool is grantable via
|
|
49475
49599
|
an API key but not callable until a key grants it). Remove it for tools that should
|
|
49476
49600
|
not be externally grantable at all.
|
|
49601
|
+
`,
|
|
49602
|
+
resources: {}
|
|
49603
|
+
},
|
|
49604
|
+
"sandbox-plugins-consume-open": {
|
|
49605
|
+
version: "1.0.0",
|
|
49606
|
+
content: `---
|
|
49607
|
+
name: sandbox-plugins-consume-open
|
|
49608
|
+
description: Call the platform's Open MCP surface from a tenant plugin handler (discover and invoke other tools/agents). Trigger: a plugin tool must orchestrate platform capabilities, call another tool or agent, or reuse an existing capability instead of re-implementing it.
|
|
49609
|
+
---
|
|
49610
|
+
|
|
49611
|
+
# Calling the platform from a handler (Open MCP)
|
|
49612
|
+
|
|
49613
|
+
A plugin handler can call **the platform's own tools and agents** through the Open
|
|
49614
|
+
MCP endpoint. Reach for this when a tool should orchestrate existing capabilities
|
|
49615
|
+
(read the knowledge base, create a task, invoke an agent) instead of
|
|
49616
|
+
re-implementing them.
|
|
49617
|
+
|
|
49618
|
+
## What the handler gets
|
|
49619
|
+
When the gateway injects the credential, each handler process starts with:
|
|
49620
|
+
- \`API_GATEWAY_URL\` \u2014 the gateway base URL.
|
|
49621
|
+
- \`OPEN_API_TOKEN\` \u2014 a short-lived bearer **scoped to this tenant's plugin callers**.
|
|
49622
|
+
|
|
49623
|
+
If the deployment does not inject them (no \`PUBLIC_GATEWAY_URL\`), the variables are
|
|
49624
|
+
**absent** \u2014 detect that and degrade gracefully (report "platform tools
|
|
49625
|
+
unavailable") rather than crashing.
|
|
49626
|
+
|
|
49627
|
+
## Discover, then call
|
|
49628
|
+
Never hardcode the tool list: the callable set is **reflected per tenant** and
|
|
49629
|
+
changes as plugins are exposed. Call \`listTools()\` at runtime.
|
|
49630
|
+
\`\`\`js
|
|
49631
|
+
const { OpenMcpClient } = require("@axiom-lattice/open-mcp-client");
|
|
49632
|
+
// or require("/root/.agents/sdk/open-mcp-client/index.js") when provisioned there
|
|
49633
|
+
|
|
49634
|
+
async function main() {
|
|
49635
|
+
const input = process.env.PLUGIN_INPUT_FILE
|
|
49636
|
+
? JSON.parse(require("fs").readFileSync(process.env.PLUGIN_INPUT_FILE, "utf8"))
|
|
49637
|
+
: JSON.parse(process.env.PLUGIN_INPUT || "{}");
|
|
49638
|
+
|
|
49639
|
+
const client = OpenMcpClient.fromEnv(); // API_GATEWAY_URL + OPEN_API_TOKEN
|
|
49640
|
+
const tools = await client.listTools(); // exactly what this handler may call
|
|
49641
|
+
const kb = await client.callTool("kb_search", { collection: "docs", query: input.q });
|
|
49642
|
+
|
|
49643
|
+
process.stdout.write(JSON.stringify({ ok: true, result: kb }));
|
|
49644
|
+
}
|
|
49645
|
+
main().catch((err) => {
|
|
49646
|
+
process.stdout.write(JSON.stringify({ ok: false, error: String(err && err.message ? err.message : err) }));
|
|
49647
|
+
});
|
|
49648
|
+
\`\`\`
|
|
49649
|
+
|
|
49650
|
+
- Tool names are \`{domain}_{action}\` (e.g. \`kb_search\`, \`task_manage_task\`).
|
|
49651
|
+
- Args follow the tool's \`inputSchema\`; \`listTools()\` returns name/description.
|
|
49652
|
+
- Return the result through the normal handler envelope (\`{ ok: true, result }\`)
|
|
49653
|
+
so it becomes this tool's output \u2014 see \`sandbox-plugins-handlers\`.
|
|
49654
|
+
|
|
49655
|
+
## What the handler may call
|
|
49656
|
+
The token carries the conservative sandbox defaults (\`sandbox_*\`, \`kb_search\`)
|
|
49657
|
+
**plus the tenant's grantable Open catalog**: every exposed tenant/global plugin
|
|
49658
|
+
tool and the tenant's agents. A call outside that set fails (tool-not-found or
|
|
49659
|
+
\`GRANT_DENIED\`); retrying will not help.
|
|
49660
|
+
|
|
49661
|
+
## Client availability
|
|
49662
|
+
The zero-dependency \`@axiom-lattice/open-mcp-client\` must be present in the
|
|
49663
|
+
sandbox: preinstalled in the image, at \`/root/.agents/sdk/open-mcp-client\`, or
|
|
49664
|
+
installed in this run (\`npm install @axiom-lattice/open-mcp-client\`). The CLI is
|
|
49665
|
+
also available: \`npx @axiom-lattice/open-mcp-client tools list --json\`.
|
|
49666
|
+
|
|
49667
|
+
## Limits
|
|
49668
|
+
- Nested sandbox orchestration is depth-limited (5); past the cap no token is
|
|
49669
|
+
re-issued, so deep recursion fails fast.
|
|
49670
|
+
- Only call what \`listTools()\` returns, and handle a missing tool gracefully.
|
|
49477
49671
|
`,
|
|
49478
49672
|
resources: {}
|
|
49479
49673
|
},
|
|
@@ -49514,10 +49708,23 @@ for the single/first connection, or \`data.connections\` (entries of
|
|
|
49514
49708
|
\`{ key, config }\`) when the plugin selects multiple connections.
|
|
49515
49709
|
|
|
49516
49710
|
## Writing output
|
|
49517
|
-
|
|
49711
|
+
Every tool handler must print **one** JSON envelope to stdout, with your payload
|
|
49712
|
+
under \`result\`:
|
|
49518
49713
|
\`\`\`js
|
|
49519
|
-
process.stdout.write(JSON.stringify({ ok: true, result:
|
|
49714
|
+
process.stdout.write(JSON.stringify({ ok: true, result: <payload> }));
|
|
49520
49715
|
\`\`\`
|
|
49716
|
+
|
|
49717
|
+
> **The payload MUST go under \`result\`.** \`result\` may be any JSON value
|
|
49718
|
+
> (object, array, string, number).
|
|
49719
|
+
> \`\`\`js
|
|
49720
|
+
> // \u2705 correct \u2014 data under \`result\`
|
|
49721
|
+
> process.stdout.write(JSON.stringify({ ok: true, result: { tasks, count } }));
|
|
49722
|
+
>
|
|
49723
|
+
> // \u274C wrong \u2014 \`result\` missing; the host surfaces the raw stdout (this
|
|
49724
|
+
> // envelope), so callers and MCP App UIs never see \`tasks\`
|
|
49725
|
+
> process.stdout.write(JSON.stringify({ ok: true, tasks, count }));
|
|
49726
|
+
> \`\`\`
|
|
49727
|
+
|
|
49521
49728
|
Non-JSON stdout is treated as a plain-text result. To fail, either print
|
|
49522
49729
|
\`{ ok: false, error }\` or exit non-zero (stderr is not required; stdout is
|
|
49523
49730
|
captured).
|
|
@@ -49703,6 +49910,8 @@ Follow the authoring skill's "Decide first" steps to confirm the goal
|
|
|
49703
49910
|
(inputs/outputs). If the UI class applies, load "sandbox-plugins-ui" before
|
|
49704
49911
|
authoring it. If the tool must be callable by external MCP clients via an API key,
|
|
49705
49912
|
load "sandbox-plugins-expose" and follow it (\`expose: true\` + key grants).
|
|
49913
|
+
If a tool must call the platform's own tools/agents from its handler, load
|
|
49914
|
+
"sandbox-plugins-consume-open".
|
|
49706
49915
|
|
|
49707
49916
|
Plan with persistent Tasks: MANDATORY until you load "task-tracking" with the
|
|
49708
49917
|
\`skill\` tool, call neither \`manage_task\` nor any other task operation. Use
|
|
@@ -49909,7 +50118,6 @@ var SANDBOX_PLUGINS_UI_REF = {
|
|
|
49909
50118
|
resource: SANDBOX_PLUGINS_UI_RESOURCE,
|
|
49910
50119
|
displayMode: "inline"
|
|
49911
50120
|
};
|
|
49912
|
-
var PLUGIN_BUILDER_SKILLS = [...Object.keys(SANDBOX_PLUGIN_SKILLS), "task-tracking"];
|
|
49913
50121
|
function tenantIdFrom(exeConfig) {
|
|
49914
50122
|
const c = exeConfig;
|
|
49915
50123
|
return c?.configurable?.runConfig?.tenantId ?? "default";
|
|
@@ -50025,9 +50233,9 @@ var sandboxPluginsPlugin = {
|
|
|
50025
50233
|
id: "skill",
|
|
50026
50234
|
type: "skill",
|
|
50027
50235
|
name: "Skill",
|
|
50028
|
-
description: "Load the sandbox-plugins authoring, patterns, manifest, handler, connection, UI, and testing skills",
|
|
50236
|
+
description: "Load any available skill, including the sandbox-plugins authoring, patterns, manifest, handler, connection, UI, and testing skills",
|
|
50029
50237
|
enabled: true,
|
|
50030
|
-
config: { readAll:
|
|
50238
|
+
config: { readAll: true }
|
|
50031
50239
|
},
|
|
50032
50240
|
{
|
|
50033
50241
|
id: "task",
|
|
@@ -50208,7 +50416,7 @@ var sandboxPluginsPlugin = {
|
|
|
50208
50416
|
assistantId: typeof runCfg.assistant_id === "string" ? runCfg.assistant_id : void 0
|
|
50209
50417
|
}
|
|
50210
50418
|
});
|
|
50211
|
-
return
|
|
50419
|
+
return handlerResultText(redactSecretValues(result, secrets));
|
|
50212
50420
|
} catch (err) {
|
|
50213
50421
|
return JSON.stringify(redactSecretValues({ ok: false, error: errorText(err) }, secrets));
|
|
50214
50422
|
}
|
|
@@ -51593,6 +51801,7 @@ registerBuiltinPlugins();
|
|
|
51593
51801
|
globSearchFiles,
|
|
51594
51802
|
grepMatchesFromFiles,
|
|
51595
51803
|
grepSearchFiles,
|
|
51804
|
+
handlerResultText,
|
|
51596
51805
|
hasChunkBuffer,
|
|
51597
51806
|
hasDelegatedWorkerToken,
|
|
51598
51807
|
hasRuntimeCapabilityPolicy,
|