@exponential_app/engine 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/capabilities.js +1 -1
- package/dist/chunks/{index-ax4aay30.js → index-3nf9f7pv.js} +2 -2
- package/dist/chunks/{index-ans80t68.js → index-3rgr16n2.js} +67 -6
- package/dist/chunks/{index-e49v2qpf.js.map → index-3rgr16n2.js.map} +1 -1
- package/dist/chunks/{index-n0acffvs.js → index-79swfbsm.js} +3 -3
- package/dist/chunks/{index-nrr8x2kq.js → index-knntt4cb.js} +2 -2
- package/dist/chunks/{index-e49v2qpf.js → index-pqpnz4ns.js} +38 -2
- package/dist/chunks/{index-ans80t68.js.map → index-pqpnz4ns.js.map} +1 -1
- package/dist/chunks/{index-t89cvabm.js → index-q4zws02e.js} +2 -2
- package/dist/chunks/{index-zw0575tv.js → index-sk8b0dh4.js} +4 -4
- package/dist/chunks/{index-zw0575tv.js.map → index-sk8b0dh4.js.map} +1 -1
- package/dist/chunks/{index-md3hz6r2.js → index-tvsykgcv.js} +3 -3
- package/dist/chunks/{index-md3hz6r2.js.map → index-tvsykgcv.js.map} +2 -2
- package/dist/chunks/{index-4ntgtkmb.js → index-v0m7r4y3.js} +90 -4
- package/dist/chunks/{index-4ntgtkmb.js.map → index-v0m7r4y3.js.map} +3 -3
- package/dist/consent.js +2 -2
- package/dist/index.js +136 -8
- package/dist/index.js.map +5 -4
- package/dist/outbox.js +1 -1
- package/dist/pdf.js +1 -1
- package/dist/providers.js +2 -2
- package/dist/runner.js +2 -2
- package/dist/tools.js +2 -2
- package/dist/types/domain-contract/src/apps.d.ts +18 -0
- package/dist/types/eengine/fixtures/providers.json +1 -1
- package/dist/types/eengine/src/capabilities.d.ts +2 -0
- package/dist/types/eengine/src/index.d.ts +1 -0
- package/dist/types/eengine/src/instance.d.ts +9 -0
- package/dist/types/eengine/src/synced.d.ts +59 -0
- package/fixtures/capabilities.json +65 -4
- package/fixtures/outbox.json +15 -1
- package/fixtures/providers.json +1 -1
- package/package.json +1 -1
- /package/dist/chunks/{index-ax4aay30.js.map → index-3nf9f7pv.js.map} +0 -0
- /package/dist/chunks/{index-n0acffvs.js.map → index-79swfbsm.js.map} +0 -0
- /package/dist/chunks/{index-nrr8x2kq.js.map → index-knntt4cb.js.map} +0 -0
- /package/dist/chunks/{index-t89cvabm.js.map → index-q4zws02e.js.map} +0 -0
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Inside this monorepo the workspace package is `@exp/eengine` (private, the same
|
|
|
15
15
|
| `consent.json` | the sheet's answer, grant writes (only the ConsentUi, only after a gesture), the Vault record, reconciling on a package load, the ask sheet and the Permissions sheet as genUI surfaces |
|
|
16
16
|
| `capabilities.json` | the capability catalog (scopes, `ask` vs `picker`, host-only), `<capability>__<tool>` / `<capability>.<tool>` names, the one broker for UI and agent calls, an agent's tool list, the audit row; capabilities v1 (E5): every tool's schemas, the bridge between the broker and a native provider (validation, app files, notification ids and taps) and the `print.pdf` layout, byte for byte |
|
|
17
17
|
| `providers.json` | the `HostConfig` (required and optional providers; no telemetry, no updates), identity kinds (local = "me"), the network policy |
|
|
18
|
-
| `outbox.json` | a `$ref` to `domain-contract` `app-records.json` `outbox` (O1, not copied) plus when it applies (sync provider = queue, none = write through) |
|
|
18
|
+
| `outbox.json` | a `$ref` to `domain-contract` `app-records.json` `outbox` (O1, not copied) plus when it applies (sync provider = queue, none = write through) and the account's queue (`syncedCases`, `SyncedRecordStore`: one outbox per account over the host's `OutboxLog`) |
|
|
19
19
|
|
|
20
20
|
## Web capabilities
|
|
21
21
|
|
package/dist/capabilities.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
capabilities_default
|
|
3
|
-
} from "./index-
|
|
3
|
+
} from "./index-3rgr16n2.js";
|
|
4
4
|
|
|
5
5
|
// packages/eengine/src/pdf.ts
|
|
6
6
|
var [PAGE_W, PAGE_H] = capabilities_default.pdf.page;
|
|
@@ -312,4 +312,4 @@ ${xref}
|
|
|
312
312
|
export { wrapLines2, pdfNumber2, pdfImage2, renderPdf2 };
|
|
313
313
|
|
|
314
314
|
//# debugId=434A59D2B116CFC664756E2164756E21
|
|
315
|
-
//# sourceMappingURL=index-
|
|
315
|
+
//# sourceMappingURL=index-3nf9f7pv.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// packages/eengine/fixtures/capabilities.json
|
|
2
2
|
var capabilities_default = {
|
|
3
|
-
$comment: "VAPP-260 (Engine E0, epic VAPP-259, docs/spec/apps.md §13.1): THE CAPABILITY CATALOG AND THE ONE BROKER. A CAPABILITY is a native tool provider the HOST implements per platform (E5) and mounts on the Engine's in-process MCP Proxy hub (packages/emcp hub.json) under its `name` (a hub server name: `hubServerNameValid`, never a `reservedNames` entry, so no capability can shadow the package's own functions or the Engine's internals). It declares SCOPES (`scopes`): a scope id `<capability>.<scope>` (`scopePattern`) is what a package's capability manifest names (package format 5 `capabilities: [{id, why, origins?}]`) and what a grant is kept for; each scope lists its TOOLS and its CONSENT: `ask` = the Engine's consent sheet once, the answer persisted (consent.json); `picker` = the platform's own picker or sheet IS the consent, every call, so nothing persists and the call needs the app in the foreground; `hostOnly: true` = a scope of the host itself (eDE's `shell.edit`, editing its ede-ui documents), NEVER in a manifest (the validator refuses it) and never granted to an app. `origins: true` = the manifest entry must list `origins` (exact `https://host[:port]` origins) and a call's `url` must be on one. NAMES: an agent calls `<capability>__<tool>` (exposes.json's qualified name, server = capability); app UI calls the function `<capability>.<tool>` (it must be in the package's `functions` allowlist like any other function); BOTH parse to the same `{capability, tool}` (`parseCases`), anything else is `unknown`. THE BROKER (`decideCases`, `brokerDecide`) is ONE function for UI and agent calls: the FIRST of: `unknown` (no such capability or tool: the agent answer is MCP's unknown-tool error -32602, never a hint) · `denied/hostOnly` · `denied/notDeclared` (the manifest is the CEILING: no scope declared = no call, no sheet) · `unavailable` (this host does not provide the capability, e.g. `camera` on a desktop without one) · `denied/originNotDeclared` (an `origins` scope whose `url` argument is missing or off the list) · for `picker`: `denied/needsForeground` in the background, else `call` · for `ask`: the stored grant `granted` = `call`, `denied` = `denied/consentDenied`, undecided = `ask` in the foreground (the Engine shows the consent sheet; consent.json `consentAnswer` turns the answer into the call's verdict) and `denied/consentNeeded` in the background (never a sheet behind the user's back). An agent's tools/list (`listCases`, `agentToolList`) = every tool of a scope the manifest declares and the host provides, `hostOnly` excluded, sorted by qualified name; a denied scope stays listed (the call answers `consentDenied`, so the agent can tell the person). THE AUDIT (`auditCases`, `brokerAuditRow`): every call, refused ones included, writes ONE row in audit.json's shape to the host's sink: `appId` = the package id, `clientId` = `ui` for app UI or the run id for an agent, `tool` = the qualified name `<capability>__<tool>` for both, `status` = `denied` for every refusal and `unknown`/`unavailable`, else audit.json's status of the call's outcome (`ok`, `isError` -> failed, `invalidParams` -> invalid, `rpcError`/`unreachable`/`timeout` -> upstream), `error` = the refusal's reason (`unknownTool` for unknown, `unavailable`), null on ok and failed; `argsHash` = audit.json's hash of the arguments as received. No hub tool writes the package, the grants or the host config: a grant changes only in consent.json's `grantWrite`. CAPABILITIES V1 (VAPP-265, E5): the TOOLS (`tools`, sorted by name) lock every tool's name, its app-facing `inputSchema`/`outputSchema` (what app UI and agents see: an agent's tools/list lists them as `<capability>__<tool>` with the same description and schemas) and, for `runs: native`, the `nativeOutputSchema` a host's native provider answers; `runs: engine` = the Engine answers it itself (`print_pdf`, no native call; the capability must still be provided). A host's native capability implements EVERY native tool of its capability (one it cannot serve answers isError `unavailable`). THE BRIDGE (`bridgeCases`, `bridgeCall`) runs between the broker's `call` verdict and the native, the same for UI and agents: (1) VALIDATE the arguments (`validateCases`) against `inputSchema` with the subset `type` (one or a list; `integer` = an integral number) · `properties` · `required` · `additionalProperties: false` · `minimum`/`maximum` · `minLength`/`maxLength` (code points) · `minItems`/`maxItems` · `items` · `enum` · `format` (`date-time` = RFC 3339 with a zone; `token` = 1 to 64 of `[A-Za-z0-9._-]`; `uri` = an absolute http(s) URL; `base64` = the standard alphabet, padded) · `dependentRequired` · `anyOf`/`oneOf` of `required` sets; checked in that order per value: type, then for an object required (listed order), unknown keys (sorted), each property (schema order), dependentRequired (its key order; the path is the MISSING key's), anyOf, oneOf; for an array minItems, maxItems, then each item; the FIRST violation answers isError `invalidParams: <JSON pointer, / for the root> <keyword>` (audit outcome `invalidParams`) and the native never runs; a missing top-level property with a `default` gets it. (2) MAP to the native arguments: photos.*, files_open = the validated arguments; files_save = `{name, mimeType, data}`; share_send = the given `title`/`text`/`url` plus `file: {name, mimeType, data}` for a `fileId`; print_print = `{name, mimeType, data}` (`name` default `document.<pdf|png|jpg>`); notifications_post = `{id: \"<app>/<id>\", title, body|null, at|null, tap}` where `tap` = lifecycle.json's `{type: notificationTap, app, screen, params ({} default)}` or null without a `screen` (a `screen` must be one of the package's screens, else `invalidParams: /screen enum`) and an absent `id` is generated (`n-` + 32 lowercase hex), so a host hands the tap back as the instance's event and ids never collide across apps; notifications_cancel = `{id: \"<app>/<id>\"}`. A `fileId` resolves through the host's BlobStore (`v1.context` = the app and its screens; missing = isError `notFound: <fileId>`), `data` = its bytes in base64; `text` = its UTF-8 bytes. `mimeType` = the given one, else `v1.mimeByExtension` of the name's last `.ext` (case-insensitive), else sniffed from the bytes (`%PDF-` application/pdf, FF D8 FF image/jpeg, the PNG signature image/png, `GIF8` image/gif), else text/plain for a `text` and application/octet-stream; print_print takes only a sniffed PDF, PNG or JPEG (else isError `unsupportedFile`). (3) CHECK the native's result: an `isError` result passes through as is (audit `failed`); otherwise its `structuredContent` must match `nativeOutputSchema` (else isError `badResult: <pointer> <keyword>`, nothing stored). (4) STORE every object in it that carries `data` (depth first, in order): BlobStore `put(app, name, mimeType, bytes)` (ids in the cases: `blob-1`, `blob-2`, …), the object loses `data` and gains `size` (bytes) and `fileId`; notifications_post answers `{id}` (the app's own id). Every answer is an MCP call result `{content: [{type: text, text: <the structuredContent as JSON>}], structuredContent}`, errors `{content: [{type: text, text: <reason>}], isError: true}`. THE PDF (`pdf`, `pdfCases`, `print_pdf`): ONE layout both twins write byte for byte (sha256 + size locked): A4 (`pdf.page` points), margins 56, content width 483, the cursor y from 786 down to 56; fonts F1 Helvetica and F2 Helvetica-Bold (Type1, WinAnsiEncoding; `pdf.widths` = their widths per WinAnsi code in 1/1000 em, `pdf.winAnsi` = the code points 0x80-0x9F map to; ASCII 0x20-0x7E and 0xA0-0xFF are themselves; a tab is a space, other controls drop, anything else is `?`). A text's width at size s = sum(widths) × s / 1000, compared exactly (sum × s ≤ 483000). Lines: a paragraph per `\\n`, words split on runs of spaces, greedy, a word wider than the line broken at the longest fitting prefix (one character at least); an empty paragraph is one empty line. A line at size s and leading L: if the page has content and y − L < 56, a new page; then `BT /F<n> <s> Tf 56 <y − s> Td (<text>) Tj ET` (an empty line writes nothing) and y −= L. heading: y −= 12 first unless the page is empty, F2 18/24, then y −= 4. text: F1 11/15, then y −= 6. image (a JPEG: SOF0-SOF3 width, height and 1/3/4 components = DeviceGray/RGB/CMYK, DCTDecode as is; a PNG: 8-bit, colour type 0 or 2, not interlaced, its IDATs concatenated with FlateDecode + DecodeParms Predictor 15; anything else isError `unsupportedImage: <fileId>`, a missing file `notFound: <fileId>`): scale = min(483/w, 730/h, 1), if the page has content and y − h·scale < 56 a new page, `q <w·scale> 0 0 <h·scale> 56 <y − h·scale> cm /Im<k> Do Q`, y −= h·scale; its caption: y −= 4, F1 9/12 lines; then y −= 6. pageBreak: a new page unless the page is empty. The last page is dropped if it is empty and not the only one. Numbers print rounded to 2 decimals, shortest form (`12`, `12.5`). Text strings escape `(`, `)` and `\\` and write bytes ≥ 0x80 as 3-digit octal. The file: `%PDF-1.4\\n%` E2 E3 CF D3 `\\n`; objects `<n> 0 obj\\n<body>\\nendobj\\n`: 1 the Catalog, 2 Pages (Kids, Count), 3 F1, 4 F2, 5 Info (`/Title <FEFF…>` UTF-16BE uppercase hex, `/Producer (Exponential Engine)`), then one XObject per image file in first-use order (`/Im1`…, a file used twice is one object), then per page its Page (MediaBox, `/Resources << /Font << /F1 3 0 R /F2 4 0 R >> /XObject << … >> >>`, the XObject dict only with the page's images in number order) and its content; a stream body is `<< … /Length <n> >>\\nstream\\n<data>\\nendstream` (content data = the page's operators joined by `\\n`); then `xref\\n0 <size>\\n0000000000 65535 f \\n`, one `<10-digit offset> 00000 n \\n` per object, `trailer\\n<< /Size <size> /Root 1 0 R /Info 5 0 R >>\\nstartxref\\n<offset>\\n%%EOF\\n`. The file is stored as `name` (default the title with `/\\:*?\"<>|` and controls replaced by `-`, + `.pdf`), application/pdf; the answer `{file, pages}`.",
|
|
3
|
+
$comment: "VAPP-260 (Engine E0, epic VAPP-259, docs/spec/apps.md §13.1): THE CAPABILITY CATALOG AND THE ONE BROKER. A CAPABILITY is a native tool provider the HOST implements per platform (E5) and mounts on the Engine's in-process MCP Proxy hub (packages/emcp hub.json) under its `name` (a hub server name: `hubServerNameValid`, never a `reservedNames` entry, so no capability can shadow the package's own functions or the Engine's internals). It declares SCOPES (`scopes`): a scope id `<capability>.<scope>` (`scopePattern`) is what a package's capability manifest names (package format 5 `capabilities: [{id, why, origins?}]`) and what a grant is kept for; each scope lists its TOOLS and its CONSENT: `ask` = the Engine's consent sheet once, the answer persisted (consent.json); `picker` = the platform's own picker or sheet IS the consent, every call, so nothing persists and the call needs the app in the foreground; `hostOnly: true` = a scope of the host itself (eDE's `shell.edit`, editing its ede-ui documents), NEVER in a manifest (the validator refuses it) and never granted to an app. `origins: true` = the manifest entry must list `origins` (exact `https://host[:port]` origins) and a call's `url` must be on one. NAMES: an agent calls `<capability>__<tool>` (exposes.json's qualified name, server = capability); app UI calls the function `<capability>.<tool>` (it must be in the package's `functions` allowlist like any other function); BOTH parse to the same `{capability, tool}` (`parseCases`), anything else is `unknown`. THE BROKER (`decideCases`, `brokerDecide`) is ONE function for UI and agent calls: the FIRST of: `unknown` (no such capability or tool: the agent answer is MCP's unknown-tool error -32602, never a hint) · `denied/hostOnly` · `denied/notDeclared` (the manifest is the CEILING: no scope declared = no call, no sheet) · `unavailable` (this host does not provide the capability, e.g. `camera` on a desktop without one) · `denied/originNotDeclared` (an `origins` scope whose `url` argument is missing or off the list) · for `picker`: `denied/needsForeground` in the background, else `call` · for `ask`: the stored grant `granted` = `call`, `denied` = `denied/consentDenied`, undecided = `ask` in the foreground (the Engine shows the consent sheet; consent.json `consentAnswer` turns the answer into the call's verdict) and `denied/consentNeeded` in the background (never a sheet behind the user's back). An agent's tools/list (`listCases`, `agentToolList`) = every tool of a scope the manifest declares and the host provides, `hostOnly` excluded, sorted by qualified name; a denied scope stays listed (the call answers `consentDenied`, so the agent can tell the person). THE AUDIT (`auditCases`, `brokerAuditRow`): every call, refused ones included, writes ONE row in audit.json's shape to the host's sink: `appId` = the package id, `clientId` = `ui` for app UI or the run id for an agent, `tool` = the qualified name `<capability>__<tool>` for both, `status` = `denied` for every refusal and `unknown`/`unavailable`, else audit.json's status of the call's outcome (`ok`, `isError` -> failed, `invalidParams` -> invalid, `rpcError`/`unreachable`/`timeout` -> upstream), `error` = the refusal's reason (`unknownTool` for unknown, `unavailable`), null on ok and failed; `argsHash` = audit.json's hash of the arguments as received. No hub tool writes the package, the grants or the host config: a grant changes only in consent.json's `grantWrite`. CAPABILITIES V1 (VAPP-265, E5): the TOOLS (`tools`, sorted by name) lock every tool's name, its app-facing `inputSchema`/`outputSchema` (what app UI and agents see: an agent's tools/list lists them as `<capability>__<tool>` with the same description and schemas) and, for `runs: native`, the `nativeOutputSchema` a host's native provider answers; `runs: engine` = the Engine answers it itself (`print_pdf`, no native call; the capability must still be provided). A host's native capability implements EVERY native tool of its capability (one it cannot serve answers isError `unavailable`). THE BRIDGE (`bridgeCases`, `bridgeCall`) runs between the broker's `call` verdict and the native, the same for UI and agents: (1) VALIDATE the arguments (`validateCases`) against `inputSchema` with the subset `type` (one or a list; `integer` = an integral number) · `properties` · `required` · `additionalProperties: false` · `minimum`/`maximum` · `minLength`/`maxLength` (code points) · `minItems`/`maxItems` · `items` · `enum` · `format` (`date-time` = RFC 3339 with a zone; `token` = 1 to 64 of `[A-Za-z0-9._-]`; `uri` = an absolute http(s) URL; `base64` = the standard alphabet, padded) · `dependentRequired` · `anyOf`/`oneOf` of `required` sets; checked in that order per value: type, then for an object required (listed order), unknown keys (sorted), each property (schema order), dependentRequired (its key order; the path is the MISSING key's), anyOf, oneOf; for an array minItems, maxItems, then each item; the FIRST violation answers isError `invalidParams: <JSON pointer, / for the root> <keyword>` (audit outcome `invalidParams`) and the native never runs; a missing top-level property with a `default` gets it. (2) MAP to the native arguments: photos.*, files_open = the validated arguments; files_save = `{name, mimeType, data}`; share_send = the given `title`/`text`/`url` plus `file: {name, mimeType, data}` for a `fileId`; print_print = `{name, mimeType, data}` (`name` default `document.<pdf|png|jpg>`); notifications_post = `{id: \"<app>/<id>\", title, body|null, at|null, tap}` where `tap` = lifecycle.json's `{type: notificationTap, app, screen, params ({} default)}` or null without a `screen` (a `screen` must be one of the package's screens, else `invalidParams: /screen enum`) and an absent `id` is generated (`n-` + 32 lowercase hex), so a host hands the tap back as the instance's event and ids never collide across apps; notifications_cancel = `{id: \"<app>/<id>\"}`. A `fileId` resolves through the host's BlobStore (`v1.context` = the app and its screens; missing = isError `notFound: <fileId>`), `data` = its bytes in base64; `text` = its UTF-8 bytes. `mimeType` = the given one, else `v1.mimeByExtension` of the name's last `.ext` (case-insensitive), else sniffed from the bytes (`%PDF-` application/pdf, FF D8 FF image/jpeg, the PNG signature image/png, `GIF8` image/gif), else text/plain for a `text` and application/octet-stream; print_print takes only a sniffed PDF, PNG or JPEG (else isError `unsupportedFile`). (3) CHECK the native's result: an `isError` result passes through as is (audit `failed`); otherwise its `structuredContent` must match `nativeOutputSchema` (else isError `badResult: <pointer> <keyword>`, nothing stored). (4) STORE every object in it that carries `data` (depth first, in order): BlobStore `put(app, name, mimeType, bytes)` (ids in the cases: `blob-1`, `blob-2`, …), the object loses `data` and gains `size` (bytes) and `fileId`; notifications_post answers `{id}` (the app's own id). Every answer is an MCP call result `{content: [{type: text, text: <the structuredContent as JSON>}], structuredContent}`, errors `{content: [{type: text, text: <reason>}], isError: true}`. THE PDF (`pdf`, `pdfCases`, `print_pdf`): ONE layout both twins write byte for byte (sha256 + size locked): A4 (`pdf.page` points), margins 56, content width 483, the cursor y from 786 down to 56; fonts F1 Helvetica and F2 Helvetica-Bold (Type1, WinAnsiEncoding; `pdf.widths` = their widths per WinAnsi code in 1/1000 em, `pdf.winAnsi` = the code points 0x80-0x9F map to; ASCII 0x20-0x7E and 0xA0-0xFF are themselves; a tab is a space, other controls drop, anything else is `?`). A text's width at size s = sum(widths) × s / 1000, compared exactly (sum × s ≤ 483000). Lines: a paragraph per `\\n`, words split on runs of spaces, greedy, a word wider than the line broken at the longest fitting prefix (one character at least); an empty paragraph is one empty line. A line at size s and leading L: if the page has content and y − L < 56, a new page; then `BT /F<n> <s> Tf 56 <y − s> Td (<text>) Tj ET` (an empty line writes nothing) and y −= L. heading: y −= 12 first unless the page is empty, F2 18/24, then y −= 4. text: F1 11/15, then y −= 6. image (a JPEG: SOF0-SOF3 width, height and 1/3/4 components = DeviceGray/RGB/CMYK, DCTDecode as is; a PNG: 8-bit, colour type 0 or 2, not interlaced, its IDATs concatenated with FlateDecode + DecodeParms Predictor 15; anything else isError `unsupportedImage: <fileId>`, a missing file `notFound: <fileId>`): scale = min(483/w, 730/h, 1), if the page has content and y − h·scale < 56 a new page, `q <w·scale> 0 0 <h·scale> 56 <y − h·scale> cm /Im<k> Do Q`, y −= h·scale; its caption: y −= 4, F1 9/12 lines; then y −= 6. pageBreak: a new page unless the page is empty. The last page is dropped if it is empty and not the only one. Numbers print rounded to 2 decimals, shortest form (`12`, `12.5`). Text strings escape `(`, `)` and `\\` and write bytes ≥ 0x80 as 3-digit octal. The file: `%PDF-1.4\\n%` E2 E3 CF D3 `\\n`; objects `<n> 0 obj\\n<body>\\nendobj\\n`: 1 the Catalog, 2 Pages (Kids, Count), 3 F1, 4 F2, 5 Info (`/Title <FEFF…>` UTF-16BE uppercase hex, `/Producer (Exponential Engine)`), then one XObject per image file in first-use order (`/Im1`…, a file used twice is one object), then per page its Page (MediaBox, `/Resources << /Font << /F1 3 0 R /F2 4 0 R >> /XObject << … >> >>`, the XObject dict only with the page's images in number order) and its content; a stream body is `<< … /Length <n> >>\\nstream\\n<data>\\nendstream` (content data = the page's operators joined by `\\n`); then `xref\\n0 <size>\\n0000000000 65535 f \\n`, one `<10-digit offset> 00000 n \\n` per object, `trailer\\n<< /Size <size> /Root 1 0 R /Info 5 0 R >>\\nstartxref\\n<offset>\\n%%EOF\\n`. The file is stored as `name` (default the title with `/\\:*?\"<>|` and controls replaced by `-`, + `.pdf`), application/pdf; the answer `{file, pages}`. EDE (#VAPP-273): Exponential Desktop's own capabilities over ede-protocol, `windows` (`windows.read`: `list`), `focus` (`focus.window`: `window`) and `launch` (`launch.app`: `app`), each `ask`, provided by eDE's shell only (`host: ede` marks them: every other host answers `unavailable`, and a platform's native suite replays only the tools of capabilities without a `host`); the host-only `shell.edit` has no v1 tool here: eDE's shell serves `shell__edit` to its own agent from its own provider, and the broker refuses it for every app. Their bridge is the plain one: the validated arguments are the native's, the native's result is the answer.",
|
|
4
4
|
scopePattern: "^[a-z][a-z0-9]*\\.[a-z][a-z0-9-]*$",
|
|
5
5
|
reservedNames: ["app", "bind", "consent", "engine", "exponential", "records", "upstream"],
|
|
6
6
|
capabilities: [
|
|
@@ -25,11 +25,23 @@ var capabilities_default = {
|
|
|
25
25
|
{ id: "files.write", verb: "save files where you pick", consent: "picker", tools: ["save"] }
|
|
26
26
|
]
|
|
27
27
|
},
|
|
28
|
+
{
|
|
29
|
+
name: "focus",
|
|
30
|
+
label: "Focus",
|
|
31
|
+
host: "ede",
|
|
32
|
+
scopes: [{ id: "focus.window", verb: "switch to your windows", consent: "ask", tools: ["window"] }]
|
|
33
|
+
},
|
|
28
34
|
{
|
|
29
35
|
name: "http",
|
|
30
36
|
label: "Network",
|
|
31
37
|
scopes: [{ id: "http.fetch", verb: "connect to {origins}", consent: "ask", origins: true, tools: ["fetch"] }]
|
|
32
38
|
},
|
|
39
|
+
{
|
|
40
|
+
name: "launch",
|
|
41
|
+
label: "Launch",
|
|
42
|
+
host: "ede",
|
|
43
|
+
scopes: [{ id: "launch.app", verb: "start applications", consent: "ask", tools: ["app"] }]
|
|
44
|
+
},
|
|
33
45
|
{
|
|
34
46
|
name: "location",
|
|
35
47
|
label: "Location",
|
|
@@ -59,6 +71,12 @@ var capabilities_default = {
|
|
|
59
71
|
name: "shell",
|
|
60
72
|
label: "Desktop",
|
|
61
73
|
scopes: [{ id: "shell.edit", verb: "change your desktop", consent: "ask", hostOnly: true, tools: ["edit"] }]
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "windows",
|
|
77
|
+
label: "Windows",
|
|
78
|
+
host: "ede",
|
|
79
|
+
scopes: [{ id: "windows.read", verb: "see your open windows", consent: "ask", tools: ["list"] }]
|
|
62
80
|
}
|
|
63
81
|
],
|
|
64
82
|
parseCases: [
|
|
@@ -216,6 +234,33 @@ var capabilities_default = {
|
|
|
216
234
|
foreground: true,
|
|
217
235
|
call: { from: "ui", name: "http_fetch", arguments: {} },
|
|
218
236
|
expected: { verdict: "denied", capability: "http", tool: "fetch", scope: "http.fetch", reason: "originNotDeclared" }
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: "eDE: a granted windows scope lists for an agent in the background",
|
|
240
|
+
manifest: [{ id: "windows.read", why: "Show the open windows." }],
|
|
241
|
+
grants: { "windows.read": "granted" },
|
|
242
|
+
provided: ["windows", "focus", "launch"],
|
|
243
|
+
foreground: false,
|
|
244
|
+
call: { from: "agent", name: "windows__list", arguments: {} },
|
|
245
|
+
expected: { verdict: "call", capability: "windows", tool: "list", scope: "windows.read", reason: null }
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: "eDE: launch is denied until the manifest declares it",
|
|
249
|
+
manifest: [{ id: "windows.read", why: "Show the open windows." }],
|
|
250
|
+
grants: {},
|
|
251
|
+
provided: ["windows", "focus", "launch"],
|
|
252
|
+
foreground: true,
|
|
253
|
+
call: { from: "ui", name: "launch_app", arguments: { app: "org.gnome.Calculator" } },
|
|
254
|
+
expected: { verdict: "denied", capability: "launch", tool: "app", scope: "launch.app", reason: "notDeclared" }
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: "eDE: focus asks once in the foreground like any ask scope",
|
|
258
|
+
manifest: [{ id: "focus.window", why: "Switch to a window from the list." }],
|
|
259
|
+
grants: {},
|
|
260
|
+
provided: ["windows", "focus", "launch"],
|
|
261
|
+
foreground: true,
|
|
262
|
+
call: { from: "ui", name: "focus_window", arguments: { window: "w3" } },
|
|
263
|
+
expected: { verdict: "ask", capability: "focus", tool: "window", scope: "focus.window", reason: null }
|
|
219
264
|
}
|
|
220
265
|
],
|
|
221
266
|
listCases: [
|
|
@@ -235,6 +280,12 @@ var capabilities_default = {
|
|
|
235
280
|
manifest: [],
|
|
236
281
|
provided: ["photos", "share"],
|
|
237
282
|
expected: []
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: "eDE's scopes list; shell.edit stays host-only",
|
|
286
|
+
manifest: [{ id: "windows.read", why: "Show the open windows." }, { id: "focus.window", why: "Switch to a window from the list." }, { id: "launch.app", why: "Start the calculator from the tile." }, { id: "shell.edit", why: "Rearrange the panel." }],
|
|
287
|
+
provided: ["windows", "focus", "launch", "shell"],
|
|
288
|
+
expected: ["focus__window", "launch__app", "windows__list"]
|
|
238
289
|
}
|
|
239
290
|
],
|
|
240
291
|
auditCases: [
|
|
@@ -297,6 +348,8 @@ var capabilities_default = {
|
|
|
297
348
|
tools: [
|
|
298
349
|
{ name: "files_open", runs: "native", description: "Let the person pick files to open. `accept` = MIME types (`image/*`) or extensions (`.csv`). Answers the files, each stored as an app file; an empty list when they cancel.", inputSchema: { type: "object", properties: { accept: { type: "array", maxItems: 20, items: { type: "string", minLength: 1, maxLength: 100 } }, multiple: { type: "boolean", default: false } }, additionalProperties: false }, nativeOutputSchema: { type: "object", properties: { files: { type: "array", maxItems: 100, items: { type: "object", properties: { name: { type: "string", minLength: 1, maxLength: 255 }, mimeType: { type: "string", minLength: 1, maxLength: 100 }, data: { type: "string", format: "base64" } }, required: ["name", "mimeType", "data"], additionalProperties: false } } }, required: ["files"], additionalProperties: false }, outputSchema: { type: "object", properties: { files: { type: "array", maxItems: 100, items: { type: "object", properties: { name: { type: "string", minLength: 1, maxLength: 255 }, mimeType: { type: "string", minLength: 1, maxLength: 100 }, size: { type: "integer", minimum: 0 }, fileId: { type: "string", minLength: 1, maxLength: 200 } }, required: ["name", "mimeType", "size", "fileId"], additionalProperties: false } } }, required: ["files"], additionalProperties: false } },
|
|
299
350
|
{ name: "files_save", runs: "native", description: "Let the person save an app file (`fileId`) or a text (`text`) where they pick. `saved` = false when they cancel.", inputSchema: { type: "object", properties: { name: { type: "string", minLength: 1, maxLength: 255 }, fileId: { type: "string", minLength: 1, maxLength: 200 }, text: { type: "string", maxLength: 1e6 }, mimeType: { type: "string", minLength: 1, maxLength: 100 } }, required: ["name"], additionalProperties: false, oneOf: [{ required: ["fileId"] }, { required: ["text"] }] }, nativeOutputSchema: { type: "object", properties: { saved: { type: "boolean" } }, required: ["saved"], additionalProperties: false }, outputSchema: { type: "object", properties: { saved: { type: "boolean" } }, required: ["saved"], additionalProperties: false } },
|
|
351
|
+
{ name: "focus_window", runs: "native", description: "Bring a window of this desktop to the front (its id from windows_list); a minimized one comes back first, a parked one brings its workspace along. `focused` = false when the window is gone.", inputSchema: { type: "object", properties: { window: { type: "string", format: "token" } }, required: ["window"], additionalProperties: false }, nativeOutputSchema: { type: "object", properties: { focused: { type: "boolean" } }, required: ["focused"], additionalProperties: false }, outputSchema: { type: "object", properties: { focused: { type: "boolean" } }, required: ["focused"], additionalProperties: false } },
|
|
352
|
+
{ name: "launch_app", runs: "native", description: "Start an installed application by its id (a desktop entry's, `org.gnome.Calculator`, with or without `.desktop`). `launched` = false when nothing is installed under that id.", inputSchema: { type: "object", properties: { app: { type: "string", minLength: 1, maxLength: 200 } }, required: ["app"], additionalProperties: false }, nativeOutputSchema: { type: "object", properties: { launched: { type: "boolean" } }, required: ["launched"], additionalProperties: false }, outputSchema: { type: "object", properties: { launched: { type: "boolean" } }, required: ["launched"], additionalProperties: false } },
|
|
300
353
|
{ name: "notifications_cancel", runs: "native", description: "Withdraw a notification this app posted or scheduled.", inputSchema: { type: "object", properties: { id: { type: "string", format: "token" } }, required: ["id"], additionalProperties: false }, nativeOutputSchema: { type: "object", properties: { cancelled: { type: "boolean" } }, required: ["cancelled"], additionalProperties: false }, outputSchema: { type: "object", properties: { cancelled: { type: "boolean" } }, required: ["cancelled"], additionalProperties: false } },
|
|
301
354
|
{ name: "notifications_post", runs: "native", description: "Post a notification now, or at `at` (an ISO time). A tap opens `screen` with `params`. Posting an `id` again replaces it.", inputSchema: { type: "object", properties: { id: { type: "string", format: "token" }, title: { type: "string", minLength: 1, maxLength: 200 }, body: { type: "string", minLength: 0, maxLength: 1000 }, at: { type: "string", format: "date-time" }, screen: { type: "string", minLength: 1, maxLength: 100 }, params: { type: "object" } }, required: ["title"], additionalProperties: false, dependentRequired: { params: ["screen"] } }, nativeOutputSchema: { type: "object", properties: {}, additionalProperties: false }, outputSchema: { type: "object", properties: { id: { type: "string", format: "token" } }, required: ["id"], additionalProperties: false } },
|
|
302
355
|
{ name: "photos_get", runs: "native", description: "Read one photo of the library by its id (from list or pick), stored as an app file.", inputSchema: { type: "object", properties: { id: { type: "string", minLength: 1, maxLength: 500 } }, required: ["id"], additionalProperties: false }, nativeOutputSchema: { type: "object", properties: { photo: { type: "object", properties: { id: { type: "string", minLength: 1, maxLength: 500 }, name: { type: "string", minLength: 1, maxLength: 255 }, mimeType: { type: "string", minLength: 1, maxLength: 100 }, takenAt: { type: ["string", "null"], format: "date-time" }, data: { type: "string", format: "base64" } }, required: ["id", "name", "mimeType", "takenAt", "data"], additionalProperties: false } }, required: ["photo"], additionalProperties: false }, outputSchema: { type: "object", properties: { photo: { type: "object", properties: { id: { type: "string", minLength: 1, maxLength: 500 }, name: { type: "string", minLength: 1, maxLength: 255 }, mimeType: { type: "string", minLength: 1, maxLength: 100 }, takenAt: { type: ["string", "null"], format: "date-time" }, size: { type: "integer", minimum: 0 }, fileId: { type: "string", minLength: 1, maxLength: 200 } }, required: ["id", "name", "mimeType", "takenAt", "size", "fileId"], additionalProperties: false } }, required: ["photo"], additionalProperties: false } },
|
|
@@ -304,7 +357,8 @@ var capabilities_default = {
|
|
|
304
357
|
{ name: "photos_pick", runs: "native", description: "Let the person pick photos in the platform's photo picker. Answers the picked photos, each stored as an app file; an empty list when they cancel.", inputSchema: { type: "object", properties: { limit: { type: "integer", minimum: 1, maximum: 50, default: 1 } }, additionalProperties: false }, nativeOutputSchema: { type: "object", properties: { photos: { type: "array", maxItems: 50, items: { type: "object", properties: { id: { type: "string", minLength: 1, maxLength: 500 }, name: { type: "string", minLength: 1, maxLength: 255 }, mimeType: { type: "string", minLength: 1, maxLength: 100 }, takenAt: { type: ["string", "null"], format: "date-time" }, data: { type: "string", format: "base64" } }, required: ["id", "name", "mimeType", "takenAt", "data"], additionalProperties: false } } }, required: ["photos"], additionalProperties: false }, outputSchema: { type: "object", properties: { photos: { type: "array", maxItems: 50, items: { type: "object", properties: { id: { type: "string", minLength: 1, maxLength: 500 }, name: { type: "string", minLength: 1, maxLength: 255 }, mimeType: { type: "string", minLength: 1, maxLength: 100 }, takenAt: { type: ["string", "null"], format: "date-time" }, size: { type: "integer", minimum: 0 }, fileId: { type: "string", minLength: 1, maxLength: 200 } }, required: ["id", "name", "mimeType", "takenAt", "size", "fileId"], additionalProperties: false } } }, required: ["photos"], additionalProperties: false } },
|
|
305
358
|
{ name: "print_pdf", runs: "engine", description: "Lay out a document (headings, text, app images, page breaks) as an A4 PDF, stored as an app file. No dialog: save, share or print the file next.", inputSchema: { type: "object", properties: { title: { type: "string", minLength: 1, maxLength: 200 }, name: { type: "string", minLength: 1, maxLength: 255 }, blocks: { type: "array", minItems: 1, maxItems: 2000, items: { type: "object", properties: { heading: { type: "string", minLength: 1, maxLength: 500 }, text: { type: "string", minLength: 0, maxLength: 20000 }, image: { type: "string", minLength: 1, maxLength: 200 }, caption: { type: "string", minLength: 1, maxLength: 500 }, pageBreak: { type: "boolean", enum: [true] } }, additionalProperties: false, oneOf: [{ required: ["heading"] }, { required: ["text"] }, { required: ["image"] }, { required: ["pageBreak"] }], dependentRequired: { caption: ["image"] } } } }, required: ["title", "blocks"], additionalProperties: false }, nativeOutputSchema: null, outputSchema: { type: "object", properties: { file: { type: "object", properties: { name: { type: "string", minLength: 1, maxLength: 255 }, mimeType: { type: "string", minLength: 1, maxLength: 100 }, size: { type: "integer", minimum: 0 }, fileId: { type: "string", minLength: 1, maxLength: 200 } }, required: ["name", "mimeType", "size", "fileId"], additionalProperties: false }, pages: { type: "integer", minimum: 1 } }, required: ["file", "pages"], additionalProperties: false } },
|
|
306
359
|
{ name: "print_print", runs: "native", description: "Open the platform's print dialog for an app file (a PDF, PNG or JPEG). `printed` = false when they cancel.", inputSchema: { type: "object", properties: { fileId: { type: "string", minLength: 1, maxLength: 200 }, name: { type: "string", minLength: 1, maxLength: 255 } }, required: ["fileId"], additionalProperties: false }, nativeOutputSchema: { type: "object", properties: { printed: { type: "boolean" } }, required: ["printed"], additionalProperties: false }, outputSchema: { type: "object", properties: { printed: { type: "boolean" } }, required: ["printed"], additionalProperties: false } },
|
|
307
|
-
{ name: "share_send", runs: "native", description: "Open the platform's share sheet with a text, a link and/or an app file. `shared` = false when they dismiss it (true where the platform cannot tell).", inputSchema: { type: "object", properties: { title: { type: "string", minLength: 1, maxLength: 200 }, text: { type: "string", minLength: 1, maxLength: 1e4 }, url: { type: "string", format: "uri" }, fileId: { type: "string", minLength: 1, maxLength: 200 }, name: { type: "string", minLength: 1, maxLength: 255 }, mimeType: { type: "string", minLength: 1, maxLength: 100 } }, additionalProperties: false, anyOf: [{ required: ["text"] }, { required: ["url"] }, { required: ["fileId"] }], dependentRequired: { fileId: ["name"], mimeType: ["fileId"] } }, nativeOutputSchema: { type: "object", properties: { shared: { type: "boolean" } }, required: ["shared"], additionalProperties: false }, outputSchema: { type: "object", properties: { shared: { type: "boolean" } }, required: ["shared"], additionalProperties: false } }
|
|
360
|
+
{ name: "share_send", runs: "native", description: "Open the platform's share sheet with a text, a link and/or an app file. `shared` = false when they dismiss it (true where the platform cannot tell).", inputSchema: { type: "object", properties: { title: { type: "string", minLength: 1, maxLength: 200 }, text: { type: "string", minLength: 1, maxLength: 1e4 }, url: { type: "string", format: "uri" }, fileId: { type: "string", minLength: 1, maxLength: 200 }, name: { type: "string", minLength: 1, maxLength: 255 }, mimeType: { type: "string", minLength: 1, maxLength: 100 } }, additionalProperties: false, anyOf: [{ required: ["text"] }, { required: ["url"] }, { required: ["fileId"] }], dependentRequired: { fileId: ["name"], mimeType: ["fileId"] } }, nativeOutputSchema: { type: "object", properties: { shared: { type: "boolean" } }, required: ["shared"], additionalProperties: false }, outputSchema: { type: "object", properties: { shared: { type: "boolean" } }, required: ["shared"], additionalProperties: false } },
|
|
361
|
+
{ name: "windows_list", runs: "native", description: "The open windows of this desktop, protected ones left out: each window's id, title, app (its desktop entry id), workspace, whether it is focused and whether it is minimized.", inputSchema: { type: "object", properties: {}, additionalProperties: false }, nativeOutputSchema: { type: "object", properties: { windows: { type: "array", maxItems: 500, items: { type: "object", properties: { id: { type: "string", format: "token" }, title: { type: "string", maxLength: 500 }, app: { type: "string", maxLength: 200 }, workspace: { type: "integer", minimum: 0 }, focused: { type: "boolean" }, minimized: { type: "boolean" } }, required: ["id", "title", "app", "workspace", "focused", "minimized"], additionalProperties: false } } }, required: ["windows"], additionalProperties: false }, outputSchema: { type: "object", properties: { windows: { type: "array", maxItems: 500, items: { type: "object", properties: { id: { type: "string", format: "token" }, title: { type: "string", maxLength: 500 }, app: { type: "string", maxLength: 200 }, workspace: { type: "integer", minimum: 0 }, focused: { type: "boolean" }, minimized: { type: "boolean" } }, required: ["id", "title", "app", "workspace", "focused", "minimized"], additionalProperties: false } } }, required: ["windows"], additionalProperties: false } }
|
|
308
362
|
],
|
|
309
363
|
validateCases: [
|
|
310
364
|
{ tool: "photos_pick", arguments: {}, expected: { arguments: { limit: 1 } } },
|
|
@@ -332,7 +386,11 @@ var capabilities_default = {
|
|
|
332
386
|
{ tool: "print_pdf", arguments: { title: "A", blocks: [{ heading: "x", text: "y" }] }, expected: { error: "invalidParams: /blocks/0 oneOf" } },
|
|
333
387
|
{ tool: "print_pdf", arguments: { title: "A", blocks: [{ text: "y", caption: "c" }] }, expected: { error: "invalidParams: /blocks/0/image dependentRequired" } },
|
|
334
388
|
{ tool: "print_pdf", arguments: { title: "A", blocks: [{ pageBreak: false }] }, expected: { error: "invalidParams: /blocks/0/pageBreak enum" } },
|
|
335
|
-
{ tool: "print_print", arguments: { fileId: 7 }, expected: { error: "invalidParams: /fileId type" } }
|
|
389
|
+
{ tool: "print_print", arguments: { fileId: 7 }, expected: { error: "invalidParams: /fileId type" } },
|
|
390
|
+
{ tool: "focus_window", arguments: {}, expected: { error: "invalidParams: /window required" } },
|
|
391
|
+
{ tool: "focus_window", arguments: { window: "w3" }, expected: { arguments: { window: "w3" } } },
|
|
392
|
+
{ tool: "launch_app", arguments: { app: "" }, expected: { error: "invalidParams: /app minLength" } },
|
|
393
|
+
{ tool: "windows_list", arguments: { since: 1 }, expected: { error: "invalidParams: /since additionalProperties" } }
|
|
336
394
|
],
|
|
337
395
|
bridgeCases: [
|
|
338
396
|
{ name: "a picked photo lands in the app's files", tool: "photos_pick", arguments: { limit: 2 }, native: { limit: 2 }, nativeResult: { structuredContent: { photos: [{ id: "ph-1", name: "IMG_0001.jpg", mimeType: "image/jpeg", takenAt: "2026-11-01T10:00:00Z", data: "aGk=" }] } }, puts: [{ name: "IMG_0001.jpg", mimeType: "image/jpeg", base64: "aGk=" }], expected: { structuredContent: { photos: [{ id: "ph-1", name: "IMG_0001.jpg", mimeType: "image/jpeg", takenAt: "2026-11-01T10:00:00Z", size: 2, fileId: "blob-1" }] } } },
|
|
@@ -359,7 +417,10 @@ var capabilities_default = {
|
|
|
359
417
|
{ name: "invalid arguments never reach the native", tool: "photos_pick", arguments: { limit: 0 }, native: null, nativeResult: null, puts: [], expected: { isError: "invalidParams: /limit minimum", outcome: "invalidParams" } },
|
|
360
418
|
{ name: "a native result off the schema is refused, nothing stored", tool: "photos_pick", arguments: {}, native: { limit: 1 }, nativeResult: { structuredContent: { photos: [{ id: "x" }] } }, puts: [], expected: { isError: "badResult: /photos/0/name required", outcome: "isError" } },
|
|
361
419
|
{ name: "bytes that are not base64 are refused", tool: "photos_get", arguments: { id: "ph-1" }, native: { id: "ph-1" }, nativeResult: { structuredContent: { photo: { id: "ph-1", name: "IMG_0001.jpg", mimeType: "image/jpeg", takenAt: "2026-11-01T10:00:00Z", data: "a b" } } }, puts: [], expected: { isError: "badResult: /photo/data format", outcome: "isError" } },
|
|
362
|
-
{ name: "a native result without structuredContent is refused", tool: "share_send", arguments: { text: "x" }, native: { text: "x" }, nativeResult: { content: [{ type: "text", text: "ok" }] }, puts: [], expected: { isError: "badResult: / type", outcome: "isError" } }
|
|
420
|
+
{ name: "a native result without structuredContent is refused", tool: "share_send", arguments: { text: "x" }, native: { text: "x" }, nativeResult: { content: [{ type: "text", text: "ok" }] }, puts: [], expected: { isError: "badResult: / type", outcome: "isError" } },
|
|
421
|
+
{ name: "eDE: the window list passes through as the native answers it", tool: "windows_list", arguments: {}, native: {}, nativeResult: { structuredContent: { windows: [{ id: "w1", title: "Firefox", app: "firefox", workspace: 0, focused: true, minimized: false }, { id: "w2", title: "", app: "org.gnome.Calculator", workspace: 1, focused: false, minimized: true }] } }, puts: [], expected: { structuredContent: { windows: [{ id: "w1", title: "Firefox", app: "firefox", workspace: 0, focused: true, minimized: false }, { id: "w2", title: "", app: "org.gnome.Calculator", workspace: 1, focused: false, minimized: true }] } } },
|
|
422
|
+
{ name: "eDE: focus names the window as given", tool: "focus_window", arguments: { window: "w2" }, native: { window: "w2" }, nativeResult: { structuredContent: { focused: true } }, puts: [], expected: { structuredContent: { focused: true } } },
|
|
423
|
+
{ name: "eDE: a launch of an unknown id is not an error, just not launched", tool: "launch_app", arguments: { app: "nope" }, native: { app: "nope" }, nativeResult: { structuredContent: { launched: false } }, puts: [], expected: { structuredContent: { launched: false } } }
|
|
363
424
|
],
|
|
364
425
|
pdf: { page: [595, 842], margin: 56, fonts: { F1: "Helvetica", F2: "Helvetica-Bold" }, widths: { Helvetica: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 333, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 0, 556, 0, 222, 556, 333, 1000, 556, 556, 333, 1000, 667, 333, 1000, 0, 611, 0, 0, 222, 222, 333, 333, 350, 556, 1000, 333, 1000, 500, 333, 944, 0, 500, 667, 278, 333, 556, 556, 556, 556, 260, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333, 333, 556, 537, 278, 333, 333, 365, 556, 834, 834, 834, 611, 667, 667, 667, 667, 667, 667, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278, 556, 556, 556, 556, 556, 556, 556, 584, 611, 556, 556, 556, 556, 500, 556, 500], "Helvetica-Bold": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 333, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 0, 556, 0, 278, 556, 500, 1000, 556, 556, 333, 1000, 667, 333, 1000, 0, 611, 0, 0, 278, 278, 500, 500, 350, 556, 1000, 333, 1000, 556, 333, 944, 0, 500, 667, 278, 333, 556, 556, 556, 556, 280, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333, 333, 611, 556, 278, 333, 333, 365, 556, 834, 834, 834, 611, 722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278, 611, 611, 611, 611, 611, 611, 611, 584, 611, 611, 611, 611, 611, 556, 611, 556] }, winAnsi: { "338": 140, "339": 156, "352": 138, "353": 154, "376": 159, "381": 142, "382": 158, "402": 131, "710": 136, "732": 152, "8211": 150, "8212": 151, "8216": 145, "8217": 146, "8218": 130, "8220": 147, "8221": 148, "8222": 132, "8224": 134, "8225": 135, "8226": 149, "8230": 133, "8240": 137, "8249": 139, "8250": 155, "8364": 128, "8482": 153 } },
|
|
365
426
|
pdfCases: [
|
|
@@ -378,5 +439,5 @@ The last evening, (quiet) \\ calm.` }] }, expected: { name: "Album.pdf", pages:
|
|
|
378
439
|
|
|
379
440
|
export { capabilities_default };
|
|
380
441
|
|
|
381
|
-
//# debugId=
|
|
382
|
-
//# sourceMappingURL=index-
|
|
442
|
+
//# debugId=09C21BFA3A9BC0B564756E2164756E21
|
|
443
|
+
//# sourceMappingURL=index-3rgr16n2.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
renderPdf2
|
|
3
|
-
} from "./index-
|
|
3
|
+
} from "./index-3nf9f7pv.js";
|
|
4
4
|
import {
|
|
5
5
|
capabilities_default
|
|
6
|
-
} from "./index-
|
|
6
|
+
} from "./index-3rgr16n2.js";
|
|
7
7
|
|
|
8
8
|
// packages/eengine/src/tools.ts
|
|
9
9
|
var CAPABILITY_TOOLS2 = capabilities_default.tools;
|
|
@@ -363,4 +363,4 @@ var pdfFileName2 = (title) => `${title.replace(/[/\\:*?"<>|\u0000-\u001f\u007f-\
|
|
|
363
363
|
export { CAPABILITY_TOOLS2, MIME_BY_EXTENSION2, toolDef2, nativeCaller2, schemaViolation2, validateToolArguments2, base64Encode2, base64Decode2, sniffMime2, mimeFor2, newNotificationId2, toolResult2, toolError2, bridgeCall2, pdfFileName2 };
|
|
364
364
|
|
|
365
365
|
//# debugId=EC2CD4A2199C0CA164756E2164756E21
|
|
366
|
-
//# sourceMappingURL=index-
|
|
366
|
+
//# sourceMappingURL=index-79swfbsm.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
canonicalJson,
|
|
3
3
|
scopeDef2
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-tvsykgcv.js";
|
|
5
5
|
// packages/eengine/fixtures/consent.json
|
|
6
6
|
var consent_default = {
|
|
7
7
|
$comment: "VAPP-260 (Engine E0, epic VAPP-259, docs/spec/apps.md §13.2): CONSENT. The package's capability manifest (format 5 `capabilities: [{id, why, origins?}]`) is the CEILING; the person's decision comes next (capabilities.json `brokerDecide`); the audit row last. GRANTS are per app: `{<scope id>: granted | denied}` (absent = undecided), kept ONLY for `ask` scopes (a `picker` scope's consent is the platform picker, every call). THE SHEET (`sheetCases`, `consentSheet`): when the broker answers `ask`, the Engine shows ONE genUI surface built from existing components only (Drawer + Text + ChoiceTiles + Button; no new primitive), the same on every platform and the default `ConsentUi` (a host may override the painter, never the rule): a bottom Drawer titled `Allow {app} to {verb}?` (`copy.title`; the scope's `verb`, an `origins` scope's `{origins}` = its origins joined with `, `), the manifest's `why` as a Text, ChoiceTiles `answer` (one column) with `copy.options` in order (`allow`, `once`, `deny`) starting on `allow`, and a Button `copy.confirm` (the default variant) whose press sends the event `consent.answer` with the picked value; closing the Drawer (Escape, the backdrop, a swipe) answers `dismiss`. THE ANSWER (`answerCases`, `consentAnswer`): `allow` = the call goes ahead and the grant is stored `granted`; `once` = the call goes ahead and nothing is stored (the next call asks again); `deny` = the call is refused `consentDenied` and `denied` is stored; `dismiss` = refused `consentDismissed`, nothing stored. MANAGE (`manageCases`, `manageSheet`): the app's Permissions surface (its … menu, the runner's app menu) = a Drawer `copy.manageTitle` with one Switch per DECLARED `ask` scope in manifest order (label = the capability's `label`, description = the `why`, checked = granted; event `consent.toggle` with the scope) and no Switch for `picker` scopes; flipping one is the gesture: on = `granted`, off = REVOKE = the grant removed (undecided, so the next call asks). WRITES (`writeCases`, `grantWrite`): a grant changes ONLY through the ConsentUi after a user gesture (`via: consentUi`, `gesture: true`); anything else (a hub tool, an agent, the package, the host config, a sync) is refused `onlyConsentUi`, a ConsentUi write without a gesture `noGesture`, an undeclared or `picker` or `hostOnly` scope `notAsk`. PERSISTENCE (`storeCases`, `grantsRecord` / `grantsOnPackage`): the Vault (emcp `Vault`: Keychain, Keystore, libsecret, the server's sealed column) holds key `vaultKey` with `{app}` = the package id, value = canonical JSON `{grants, origins, signer, v}`: `v` = `recordVersion`, `signer` = the package signature's public key, `origins` = the `http.fetch` origins the grant was given for (sorted). On every package load the Engine reconciles (`grantsOnPackage`): another signer = a different author, every grant dropped; a scope the new version no longer declares (or now declares as non-ask) is dropped; an `http.fetch` grant whose declared origins are no longer a subset of the granted ones is dropped (new origins ask again); an unreadable record or another `v` = no grants (ask again, never fail open).",
|
|
@@ -256,4 +256,4 @@ function manageSheet2(manifest, grants) {
|
|
|
256
256
|
export { CONSENT_COPY2, GRANTS_RECORD_VERSION2, consentAnswer2, grantWrite2, grantsRecord2, grantsOnPackage2, consentSheet2, manageSheet2 };
|
|
257
257
|
|
|
258
258
|
//# debugId=45BA72763170257B64756E2164756E21
|
|
259
|
-
//# sourceMappingURL=index-
|
|
259
|
+
//# sourceMappingURL=index-knntt4cb.js.map
|
|
@@ -1281,6 +1281,39 @@ var contract_default = {
|
|
|
1281
1281
|
subjectKey: "id",
|
|
1282
1282
|
result: "agent"
|
|
1283
1283
|
},
|
|
1284
|
+
{
|
|
1285
|
+
name: "agents_create",
|
|
1286
|
+
title: "Create agent",
|
|
1287
|
+
blurb: "Adds an agent to a team, with its device, instructions, skills and triggers.",
|
|
1288
|
+
progressive: "Creating agent",
|
|
1289
|
+
done: "Created agent",
|
|
1290
|
+
progressiveMany: "Creating {n} agents",
|
|
1291
|
+
doneMany: "Created {n} agents",
|
|
1292
|
+
subjectKey: "name",
|
|
1293
|
+
result: "agent"
|
|
1294
|
+
},
|
|
1295
|
+
{
|
|
1296
|
+
name: "agents_update",
|
|
1297
|
+
title: "Update agent",
|
|
1298
|
+
blurb: "Changes an agent's name, instructions, skills, device, login, model or triggers.",
|
|
1299
|
+
progressive: "Updating agent",
|
|
1300
|
+
done: "Updated agent",
|
|
1301
|
+
progressiveMany: "Updating {n} agents",
|
|
1302
|
+
doneMany: "Updated {n} agents",
|
|
1303
|
+
subjectKey: "id",
|
|
1304
|
+
result: "agent"
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
name: "agents_delete",
|
|
1308
|
+
title: "Delete agent",
|
|
1309
|
+
blurb: "Removes an agent and its conversations.",
|
|
1310
|
+
progressive: "Deleting agent",
|
|
1311
|
+
done: "Deleted agent",
|
|
1312
|
+
progressiveMany: "Deleting {n} agents",
|
|
1313
|
+
doneMany: "Deleted {n} agents",
|
|
1314
|
+
subjectKey: "id",
|
|
1315
|
+
result: "none"
|
|
1316
|
+
},
|
|
1284
1317
|
{
|
|
1285
1318
|
name: "prompts_list",
|
|
1286
1319
|
title: "List prompts",
|
|
@@ -2108,6 +2141,9 @@ var contract_default = {
|
|
|
2108
2141
|
{ name: "devices_account_login", refs: "" },
|
|
2109
2142
|
{ name: "agents_list", refs: "list:agent@" },
|
|
2110
2143
|
{ name: "agents_get", refs: "agent@" },
|
|
2144
|
+
{ name: "agents_create", refs: "agent@" },
|
|
2145
|
+
{ name: "agents_update", refs: "agent@" },
|
|
2146
|
+
{ name: "agents_delete", refs: "" },
|
|
2111
2147
|
{ name: "prompts_list", refs: "list:prompt@" },
|
|
2112
2148
|
{ name: "prompts_get", refs: "prompt@" },
|
|
2113
2149
|
{ name: "prompts_create", refs: "prompt@" },
|
|
@@ -2173,5 +2209,5 @@ var contract_default = {
|
|
|
2173
2209
|
|
|
2174
2210
|
export { contract_default };
|
|
2175
2211
|
|
|
2176
|
-
//# debugId=
|
|
2177
|
-
//# sourceMappingURL=index-
|
|
2212
|
+
//# debugId=1F22159895C09CF364756E2164756E21
|
|
2213
|
+
//# sourceMappingURL=index-pqpnz4ns.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
scopeDef2
|
|
3
|
-
} from "./index-
|
|
3
|
+
} from "./index-tvsykgcv.js";
|
|
4
4
|
|
|
5
5
|
// packages/eengine/src/runner.ts
|
|
6
6
|
import { navScreens, packageScreens } from "@exponential_app/eui/host";
|
|
@@ -2298,4 +2298,4 @@ function runnerToast2(message) {
|
|
|
2298
2298
|
export { RUNNER_COPY2, RUNNER_BUNDLE2, RUNNER_SHELL2, runnerConfig2, runnerHasPermissions2, runnerPackage2, runnerFailure2, runnerToast2 };
|
|
2299
2299
|
|
|
2300
2300
|
//# debugId=8614238A2FCD206064756E2164756E21
|
|
2301
|
-
//# sourceMappingURL=index-
|
|
2301
|
+
//# sourceMappingURL=index-q4zws02e.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
capabilityNameValid2
|
|
3
|
-
} from "./index-
|
|
3
|
+
} from "./index-tvsykgcv.js";
|
|
4
4
|
|
|
5
5
|
// packages/eengine/src/providers.ts
|
|
6
6
|
import { recordRefusal } from "@exponential_app/eui/host";
|
|
@@ -28,7 +28,7 @@ var providers_default = {
|
|
|
28
28
|
hosts: {
|
|
29
29
|
exponential: { host: {}, storage: {}, blobs: {}, identity: { kind: "member" }, vault: {}, window: {}, zone: {}, network: "any", sync: {}, agents: [{ id: "acp" }, { id: "remoteSession" }], capabilities: [{ name: "files" }, { name: "share" }, { name: "notifications" }] },
|
|
30
30
|
softcoded: { host: {}, storage: {}, blobs: {}, identity: { kind: "local" }, vault: {}, window: {}, network: "upstreamsOnly", agents: [{ id: "localModel" }] },
|
|
31
|
-
ede: { host: {}, storage: {}, blobs: {}, identity: { kind: "local" }, vault: {}, window: {}, network: "any", agents: [{ id: "acp" }], capabilities: [{ name: "files" }, { name: "notifications" }, { name: "shell" }] },
|
|
31
|
+
ede: { host: {}, storage: {}, blobs: {}, identity: { kind: "local" }, vault: {}, window: {}, network: "any", agents: [{ id: "acp" }], capabilities: [{ name: "files" }, { name: "notifications" }, { name: "shell" }, { name: "windows" }, { name: "focus" }, { name: "launch" }] },
|
|
32
32
|
spezl: { host: {}, storage: {}, blobs: {}, identity: { kind: "peer" }, vault: {}, window: {}, network: "any", sync: {}, agents: [{ id: "acp" }] },
|
|
33
33
|
exported: { host: {}, storage: {}, blobs: {}, identity: { kind: "local" }, vault: {}, window: {}, network: "off", capabilities: [{ name: "photos" }, { name: "print" }, { name: "share" }] }
|
|
34
34
|
},
|
|
@@ -122,5 +122,5 @@ function networkAllows2(policy, target, declared) {
|
|
|
122
122
|
|
|
123
123
|
export { PROVIDER_KEYS2, FORBIDDEN_HOST_KEYS2, NETWORK_POLICIES2, IDENTITY_KINDS2, AGENT_PROVIDERS2, validateHostConfig2, engineViewer2, engineRecordRefusal2, networkAllows2 };
|
|
124
124
|
|
|
125
|
-
//# debugId=
|
|
126
|
-
//# sourceMappingURL=index-
|
|
125
|
+
//# debugId=CEF389E0C0D8929064756E2164756E21
|
|
126
|
+
//# sourceMappingURL=index-sk8b0dh4.js.map
|
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
"// VAPP-260: what a host hands the Engine (`HostConfig`), the viewer each\n// identity kind yields, and the network policy. Locked by `fixtures/providers.json`.\n\nimport { recordRefusal, type RecordOp, type RecordOwnership, type RecordPolicy, type RecordViewer } from \"@exponential_app/eui/host\"\n\nimport fixture from \"../fixtures/providers.json\" with { type: \"json\" }\nimport { capabilityNameValid } from \"./capabilities\"\n\nexport type ProviderKey = keyof typeof fixture.providers\nexport type NetworkPolicy = `off` | `upstreamsOnly` | `any`\nexport type IdentityKind = `member` | `local` | `peer`\nexport type AgentProviderId = `acp` | `localModel` | `remoteSession`\n\nexport const PROVIDER_KEYS = Object.keys(fixture.providers) as ProviderKey[]\nexport const FORBIDDEN_HOST_KEYS: readonly string[] = fixture.forbidden\nexport const NETWORK_POLICIES = fixture.networkPolicies as readonly NetworkPolicy[]\nexport const IDENTITY_KINDS = fixture.identityKinds as readonly IdentityKind[]\nexport const AGENT_PROVIDERS = fixture.agentProviders as readonly AgentProviderId[]\n\nexport interface HostConfigIssue {\n key: string\n code: `missing` | `forbidden` | `unknown` | `invalid` | `duplicateAgent` | `duplicateCapability` | `invalidCapability`\n}\n\nconst isObject = (v: unknown): v is Record<string, unknown> => !!v && typeof v === `object` && !Array.isArray(v)\n\n/** Every problem with a host config: providers in order, then forbidden/unknown keys sorted. */\nexport function validateHostConfig(config: Record<string, unknown>): HostConfigIssue[] {\n const issues: HostConfigIssue[] = []\n for (const key of PROVIDER_KEYS) {\n const def = fixture.providers[key] as { required: boolean }\n const value = config[key]\n if (value === undefined) {\n if (def.required) issues.push({ key, code: `missing` })\n continue\n }\n if (key === `identity` && !(isObject(value) && (IDENTITY_KINDS as readonly unknown[]).includes(value.kind))) issues.push({ key, code: `invalid` })\n if (key === `network` && !(NETWORK_POLICIES as readonly unknown[]).includes(value)) issues.push({ key, code: `invalid` })\n if (key === `agents`) {\n const ids = Array.isArray(value) ? value.map((a) => (isObject(a) ? a.id : undefined)) : null\n if (!ids || ids.some((id) => !(AGENT_PROVIDERS as readonly unknown[]).includes(id))) issues.push({ key, code: `invalid` })\n else if (new Set(ids).size !== ids.length) issues.push({ key, code: `duplicateAgent` })\n }\n if (key === `capabilities`) {\n const names = Array.isArray(value) ? value.map((c) => (isObject(c) ? c.name : undefined)) : null\n if (!names || names.some((n) => typeof n !== `string` || !capabilityNameValid(n))) issues.push({ key, code: `invalidCapability` })\n else if (new Set(names).size !== names.length) issues.push({ key, code: `duplicateCapability` })\n }\n }\n const extra = Object.keys(config)\n .filter((k) => !(PROVIDER_KEYS as readonly string[]).includes(k))\n .sort()\n for (const key of extra) issues.push({ key, code: FORBIDDEN_HOST_KEYS.includes(key) ? `forbidden` : `unknown` })\n return issues\n}\n\nexport type EngineIdentity =\n | { kind: `member`; userId: string; role: string | null; access?: `none` | `read` | `write` }\n | { kind: `local`; deviceId: string }\n | { kind: `peer`; key: string }\n\n/** The records viewer of an identity: local = the one owner of everything (\"me\"). */\nexport function engineViewer(identity: EngineIdentity): Required<RecordViewer> {\n if (identity.kind === `local`) return { id: identity.deviceId, role: `owner`, access: `write` }\n if (identity.kind === `peer`) return { id: identity.key, role: `member`, access: `write` }\n return { id: identity.userId, role: identity.role, access: identity.access ?? `write` }\n}\n\n/** Why the viewer of `identity` may not `op` (null = allowed). */\nexport const engineRecordRefusal = (identity: EngineIdentity, policy: RecordPolicy, op: RecordOp, row?: RecordOwnership | null): string | null =>\n recordRefusal(policy, op, engineViewer(identity), row)\n\n/** May the instance reach `target` under `policy`? `declared` = the package declares the upstream (or the origin). */\nexport function networkAllows(policy: NetworkPolicy, target: `upstream` | `sync` | `http`, declared: boolean): { allowed: boolean; reason: string | null } {\n if (policy === `off`) return { allowed: false, reason: `offline` }\n if (target === `upstream` && !declared) return { allowed: false, reason: `notDeclared` }\n if (target === `http` && policy === `upstreamsOnly`) return { allowed: false, reason: `network` }\n return { allowed: true, reason: null }\n}\n"
|
|
6
6
|
],
|
|
7
7
|
"mappings": ";;;;;AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAUO,IAAM,iBAAgB,OAAO,KAAK,kBAAQ,SAAS;AACnD,IAAM,uBAAyC,kBAAQ;AACvD,IAAM,oBAAmB,kBAAQ;AACjC,IAAM,kBAAiB,kBAAQ;AAC/B,IAAM,mBAAkB,kBAAQ;AAOvC,IAAM,WAAW,CAAC,MAA6C,CAAC,CAAC,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC;AAGxG,SAAS,mBAAkB,CAAC,QAAoD;AAAA,EACrF,MAAM,SAA4B,CAAC;AAAA,EACnC,WAAW,OAAO,gBAAe;AAAA,IAC/B,MAAM,MAAM,kBAAQ,UAAU;AAAA,IAC9B,MAAM,QAAQ,OAAO;AAAA,IACrB,IAAI,UAAU,WAAW;AAAA,MACvB,IAAI,IAAI;AAAA,QAAU,OAAO,KAAK,EAAE,KAAK,MAAM,UAAU,CAAC;AAAA,MACtD;AAAA,IACF;AAAA,IACA,IAAI,QAAQ,cAAc,EAAE,SAAS,KAAK,KAAM,gBAAsC,SAAS,MAAM,IAAI;AAAA,MAAI,OAAO,KAAK,EAAE,KAAK,MAAM,UAAU,CAAC;AAAA,IACjJ,IAAI,QAAQ,aAAa,CAAE,kBAAwC,SAAS,KAAK;AAAA,MAAG,OAAO,KAAK,EAAE,KAAK,MAAM,UAAU,CAAC;AAAA,IACxH,IAAI,QAAQ,UAAU;AAAA,MACpB,MAAM,MAAM,MAAM,QAAQ,KAAK,IAAI,MAAM,IAAI,CAAC,MAAO,SAAS,CAAC,IAAI,EAAE,KAAK,SAAU,IAAI;AAAA,MACxF,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAE,iBAAuC,SAAS,EAAE,CAAC;AAAA,QAAG,OAAO,KAAK,EAAE,KAAK,MAAM,UAAU,CAAC;AAAA,MACzH,KAAK,IAAI,IAAI,IAAI,GAAG,EAAE,SAAS,IAAI;AAAA,QAAQ,OAAO,KAAK,EAAE,KAAK,MAAM,iBAAiB,CAAC;AAAA,IACxF;AAAA,IACA,IAAI,QAAQ,gBAAgB;AAAA,MAC1B,MAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,MAAM,IAAI,CAAC,MAAO,SAAS,CAAC,IAAI,EAAE,OAAO,SAAU,IAAI;AAAA,MAC5F,IAAI,CAAC,SAAS,MAAM,KAAK,CAAC,MAAM,OAAO,MAAM,YAAY,CAAC,qBAAoB,CAAC,CAAC;AAAA,QAAG,OAAO,KAAK,EAAE,KAAK,MAAM,oBAAoB,CAAC;AAAA,MACjI,KAAK,IAAI,IAAI,IAAI,KAAK,EAAE,SAAS,MAAM;AAAA,QAAQ,OAAO,KAAK,EAAE,KAAK,MAAM,sBAAsB,CAAC;AAAA,IACjG;AAAA,EACF;AAAA,EACA,MAAM,QAAQ,OAAO,KAAK,MAAM,EAC7B,OAAO,CAAC,MAAM,CAAE,eAAoC,SAAS,CAAC,CAAC,EAC/D,KAAK;AAAA,EACR,WAAW,OAAO;AAAA,IAAO,OAAO,KAAK,EAAE,KAAK,MAAM,qBAAoB,SAAS,GAAG,IAAI,cAAc,UAAU,CAAC;AAAA,EAC/G,OAAO;AAAA;AASF,SAAS,aAAY,CAAC,UAAkD;AAAA,EAC7E,IAAI,SAAS,SAAS;AAAA,IAAS,OAAO,EAAE,IAAI,SAAS,UAAU,MAAM,SAAS,QAAQ,QAAQ;AAAA,EAC9F,IAAI,SAAS,SAAS;AAAA,IAAQ,OAAO,EAAE,IAAI,SAAS,KAAK,MAAM,UAAU,QAAQ,QAAQ;AAAA,EACzF,OAAO,EAAE,IAAI,SAAS,QAAQ,MAAM,SAAS,MAAM,QAAQ,SAAS,UAAU,QAAQ;AAAA;AAIjF,IAAM,uBAAsB,CAAC,UAA0B,QAAsB,IAAc,QAChG,cAAc,QAAQ,IAAI,cAAa,QAAQ,GAAG,GAAG;AAGhD,SAAS,cAAa,CAAC,QAAuB,QAAsC,UAAgE;AAAA,EACzJ,IAAI,WAAW;AAAA,IAAO,OAAO,EAAE,SAAS,OAAO,QAAQ,UAAU;AAAA,EACjE,IAAI,WAAW,cAAc,CAAC;AAAA,IAAU,OAAO,EAAE,SAAS,OAAO,QAAQ,cAAc;AAAA,EACvF,IAAI,WAAW,UAAU,WAAW;AAAA,IAAiB,OAAO,EAAE,SAAS,OAAO,QAAQ,UAAU;AAAA,EAChG,OAAO,EAAE,SAAS,MAAM,QAAQ,KAAK;AAAA;",
|
|
8
|
-
"debugId": "
|
|
8
|
+
"debugId": "CEF389E0C0D8929064756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
contract_default
|
|
3
|
-
} from "./index-
|
|
3
|
+
} from "./index-pqpnz4ns.js";
|
|
4
4
|
import {
|
|
5
5
|
capabilities_default
|
|
6
|
-
} from "./index-
|
|
6
|
+
} from "./index-3rgr16n2.js";
|
|
7
7
|
// packages/domain-contract/fixtures/app-actions.json
|
|
8
8
|
var app_actions_default = {
|
|
9
9
|
$comment: "VAPP-120 typed host ACTIONS ×4: every Exponential MCP operation is a host function an app may call, `exponential.<noun>.<op>` (the apps noun's ops included; the `exponential_sessions_send` verb tool = `exponential_sessions_send`; `session_self` and `guide_write` are not offered: they need an agent's session), run by the client's app host as a tools/call on the instance's MCP as the viewer (the `harness_mcp` transport, arguments = {op, …args}; no `op` for the verb tool). A package lists what it calls in `functions` (an exact name or a `prefix*` pattern: `exponential_issues_*`); the SDK denies anything else. GRANTS (the 2026-10-24 ruling, a conservative default): only a team OWNER (`grantRole`; `canGrantActions(role)`) sets or changes an app's grants, the app's creator gets no grant power from creating it; a DESTRUCTIVE operation (`destructiveWords`: a word of the tool name or op is delete, merge, close, end, remove, trash; `actionDestructive`, `actionGrantable` = not destructive) can NEVER be granted: the server refuses it, every host ignores a stored grant for it (`grantAllows` is false), and it always shows the consent card with the operation's words, for every viewer; the settings row of a destructive action shows `copy.tools.always` in place of the switch, a non-owner sees the switches disabled. `grantableCases` and `canGrantCases` replay ×4 beside `grantCases` (which carry: an owner's grant skips the card for a non-destructive op, a destructive op ignores a grant). A DESTRUCTIVE CALL (`destructiveArgs`: the operations prompts.json flags destructive that ride on an argument of a grantable action: `exponential_sessions_send` with `stop` set = the `stop-run` prompt) ignores a grant the same way and shows the destructive card (`callDestructive(name, args)`; `grantAllows(granted, name, args)` takes the call's args, an arg present and not false/null counts); the action itself stays grantable for its other calls. `apps.granted_tools` (synced on the apps shape, a list of exact function names) = what the app may run as the viewer WITHOUT asking: the app's SETTINGS page (web + desktop; phones have no app settings) shows a `copy.tools` group under the records line with one row per declared function (`declaredActions`: patterns expand against `functions`), the display row's title (contract `expToolDisplay`, `<noun>_<op>`) + the function name muted + a Switch (an owner; others see it disabled; a destructive action shows `copy.tools.always` instead), `copy.tools.none` when the package declares none, `copy.tools.hint` under the title. The server (`apps.update`, an owner only) accepts a grant only for a non-destructive action the HEAD package declares, and every new version (upsert, restore) drops the grants it no longer declares: a grant is a choice about the current package, never inherited by a later version that re-declares the action; a version written by anyone but a team OWNER (a member's upsert or restore) clears ALL the grants, so a non-owner's code never runs on an owner's grant without the consent card, while an owner's own new version keeps the grants it still declares. A typed call's `op` is the NAME's: an `op` key in the args never changes the operation (×4). CONSENT: a declared, ungranted call shows the consent card worded for the operation (`actionConsent`: title `Allow this surface to <title, lower-cased><subject>?` where subject = the arg named by the row's `subjectKey` when it is a string; the destructive variant when a word of the tool name or op is delete, merge, close, end, remove, trash; the arguments block = the args without `op`), Allow runs it ONCE; a granted call runs without a card; `harness_mcp` keeps the generic copy and always asks. The TS reference `app-actions.ts` is replayed by desktop `domain::app_actions`, iOS `AppActions.swift`, Android `AppActions.kt` (`parseCases`, `grantCases`, `declaredCases`, `consentCases`); `functions` is the ×4 list (the web gates it against its noun table). VAPP-158 OWNER ACTIONS (`ownerActions`, `actionOwnerOnly`): the non-destructive operations the server runs for a team OWNER only (adding a member, minting or revoking an invite, adding an MCP server, creating or editing a prompt, changing the team's settings) are never grantable either: `actionGrantable` is false, the server refuses the grant, every host ignores a stored one (`grantAllows` false) and the call always shows the consent card (the plain card, not the destructive variant: `callDestructive` is unchanged), so an app never runs an owner's operation card-free for anyone, owners included; the settings row shows `copy.tools.always` like a destructive one. `ownerOnlyCases` replay ×4. EXP-1657 ALIASES (`aliases`, old name → new name): the retired `exponential_team_config` tool split into its nouns (labels, statuses, prompts, agents, mcp_servers, repositories; `mcp_servers_add` = `mcp_servers.create`, `mcp_servers_remove` = `mcp_servers.delete`, `repositories_add` = `repositories.create`), so `exponential.team_config.<x>_<y>` became `exponential.<noun>.<op>`. An already-published package keeps working: every host registers each alias as a function too (`actionAliases`), an alias PARSES to its new name's target, and everything else follows the NEW name (`canonicalAction`): destructive, owner-only, grantable, the display title and the consent card; a grant matches when the grant and the call name the same canonical action (a grant stored under an old name counts for the new one and vice versa); `declaredActions` expands a package's names and patterns over the aliases too and returns their NEW names. Aliases are never listed in `functions`, `tools` or a declared list. Alias cases sit in `parseCases`, `grantCases`, `grantableCases`, `ownerOnlyCases`, `declaredCases` and `consentCases` (`$why` names them).",
|
|
@@ -3012,4 +3012,4 @@ function brokerAuditRow2(input) {
|
|
|
3012
3012
|
export { canonicalJson, CAPABILITIES2, RESERVED_CAPABILITY_NAMES2, scopeIdValid2, capabilityNameValid2, capabilityDef2, scopeDef2, parseCapabilityCall2, urlOrigin2, brokerDecide2, agentToolList2, brokerAuditRow2 };
|
|
3013
3013
|
|
|
3014
3014
|
//# debugId=7E5EC23E0DE4BB9C64756E2164756E21
|
|
3015
|
-
//# sourceMappingURL=index-
|
|
3015
|
+
//# sourceMappingURL=index-tvsykgcv.js.map
|