@rubytech/create-sitedesk-code 0.1.506 → 0.1.508
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/package.json +2 -2
- package/payload/platform/config/brand.json +101 -18
- package/payload/platform/lib/account-schema-regions/dist/index.d.ts +17 -5
- package/payload/platform/lib/account-schema-regions/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/account-schema-regions/dist/index.js +20 -6
- package/payload/platform/lib/account-schema-regions/dist/index.js.map +1 -1
- package/payload/platform/lib/account-schema-regions/src/index.ts +20 -6
- package/payload/platform/plugins/admin/PLUGIN.md +46 -5
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +138 -14
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +14 -0
- package/payload/platform/plugins/aeo/PLUGIN.md +3 -0
- package/payload/platform/plugins/browser/PLUGIN.md +18 -0
- package/payload/platform/plugins/connector/PLUGIN.md +4 -0
- package/payload/platform/plugins/contacts/PLUGIN.md +9 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +111 -13
- package/payload/platform/plugins/docs/references/plugins-guide.md +26 -0
- package/payload/platform/plugins/email/PLUGIN.md +17 -0
- package/payload/platform/plugins/filesystem/PLUGIN.md +6 -0
- package/payload/platform/plugins/google/PLUGIN.md +12 -0
- package/payload/platform/plugins/graph/PLUGIN.md +3 -0
- package/payload/platform/plugins/graph-viewer/PLUGIN.md +1 -0
- package/payload/platform/plugins/ledger/PLUGIN.md +7 -0
- package/payload/platform/plugins/memory/PLUGIN.md +47 -3
- package/payload/platform/plugins/outlook/PLUGIN.md +24 -0
- package/payload/platform/plugins/quickbooks/PLUGIN.md +15 -0
- package/payload/platform/plugins/replicate/PLUGIN.md +3 -0
- package/payload/platform/plugins/scheduling/PLUGIN.md +10 -0
- package/payload/platform/plugins/storage-broker/PLUGIN.md +12 -0
- package/payload/platform/plugins/telegram/PLUGIN.md +3 -0
- package/payload/platform/plugins/url-get/PLUGIN.md +1 -0
- package/payload/platform/plugins/voice-mirror/PLUGIN.md +5 -0
- package/payload/platform/plugins/whatsapp/PLUGIN.md +12 -0
- package/payload/platform/plugins/work/PLUGIN.md +15 -0
- package/payload/platform/plugins/workflows/PLUGIN.md +8 -0
- package/payload/platform/scripts/__tests__/provision-honours-disabled-agents.test.sh +84 -0
- package/payload/platform/scripts/check-risk-class.mjs +150 -0
- package/payload/platform/scripts/check-specialist-tool-surface.mjs +5 -2
- package/payload/platform/scripts/lib/__pycache__/account-schema-owned-dirs.cpython-314.pyc +0 -0
- package/payload/platform/scripts/lib/canonical-tool-names.mjs +7 -4
- package/payload/platform/scripts/lib/provision-account-dir.sh +47 -1
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts +1 -1
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js +56 -3
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +4 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +8 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/tool-surface.d.ts +32 -5
- package/payload/platform/services/claude-session-manager/dist/tool-surface.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/tool-surface.js +83 -19
- package/payload/platform/services/claude-session-manager/dist/tool-surface.js.map +1 -1
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/PLUGIN.md +4 -0
- package/payload/server/{chunk-5ADBNEJA.js → chunk-2WAXM5N2.js} +111 -111
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/activity.html +5 -5
- package/payload/server/public/agents.html +4 -4
- package/payload/server/public/assets/{AdminLoginScreens-D5KOQA8S.js → AdminLoginScreens-DKeCt1uf.js} +1 -1
- package/payload/server/public/assets/{AdminShell-C8wazpnz.js → AdminShell-HeELCCjJ.js} +1 -1
- package/payload/server/public/assets/{activity-CFqkDhTs.js → activity-BEIDAxDA.js} +1 -1
- package/payload/server/public/assets/admin-C0-WU1sf.js +1 -0
- package/payload/server/public/assets/agents-DjUvMFTj.js +1 -0
- package/payload/server/public/assets/{browser-T_5xbnJh.js → browser-GkVTGqlb.js} +1 -1
- package/payload/server/public/assets/{calendar-CKjuiG8c.js → calendar-CSqJeFEg.js} +1 -1
- package/payload/server/public/assets/chat-C0Ps84U5.js +1 -0
- package/payload/server/public/assets/chevron-left-DQ2uNlu9.js +1 -0
- package/payload/server/public/assets/chevron-right-C4QCiRGD.js +1 -0
- package/payload/server/public/assets/clock-BEeQ7JxX.js +1 -0
- package/payload/server/public/assets/data-CGuNfym7.js +1 -0
- package/payload/server/public/assets/{file-text-c6cA4Evd.js → file-text-DiscM2pP.js} +1 -1
- package/payload/server/public/assets/{graph-DIk-o45J.js → graph-CRP5DM8C.js} +1 -1
- package/payload/server/public/assets/{graph-labels-5bjxhC_p.js → graph-labels-Du-6KoBE.js} +1 -1
- package/payload/server/public/assets/{maximize-2-BCQkFb1_.js → maximize-2-D-H_uIV-.js} +1 -1
- package/payload/server/public/assets/{operator-0UnNo2ms.js → operator-D5rA76YY.js} +1 -1
- package/payload/server/public/assets/page-CHK1415C.js +1 -0
- package/payload/server/public/assets/{page-HVtUfub2.js → page-DZE3H_n4.js} +4 -4
- package/payload/server/public/assets/{public-CKEYyIPn.js → public-DFHXVkdD.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-dTpO5dhL.js → rotate-ccw-CesEJsuO.js} +1 -1
- package/payload/server/public/assets/{routines-C6P1-Dto.js → routines-DYMYY_r8.js} +1 -1
- package/payload/server/public/assets/{skills-ClrpVqlz.js → skills-67jtJ16D.js} +1 -1
- package/payload/server/public/assets/{tasks-C8SNhSFC.js → tasks-D2C2a-uk.js} +1 -1
- package/payload/server/public/assets/{time-entry-format-CCO6N6XZ.js → time-entry-format-BixTGQAd.js} +1 -1
- package/payload/server/public/assets/{triangle-alert-CHjdKpag.js → triangle-alert-Co9RWb9q.js} +1 -1
- package/payload/server/public/assets/{useCopyFeedback-BTUu3-l8.js → useCopyFeedback-7zwf9_w4.js} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-Bg72hdQ8.js +9 -0
- package/payload/server/public/assets/useSubAccountSwitcher-DE4v5_Fz.css +1 -0
- package/payload/server/public/assets/{useVoiceRecorder-BeNqxmm2.js → useVoiceRecorder-CZDDiwMD.js} +1 -1
- package/payload/server/public/assets/{wrench-CeNnc7oO.js → wrench-CjQqdj4P.js} +1 -1
- package/payload/server/public/brand/fonts/inter-400.woff2 +0 -0
- package/payload/server/public/brand/fonts/inter-500.woff2 +0 -0
- package/payload/server/public/brand/fonts/inter-600.woff2 +0 -0
- package/payload/server/public/brand/fonts/inter-700.woff2 +0 -0
- package/payload/server/public/brand/fonts/inter-800.woff2 +0 -0
- package/payload/server/public/brand-defaults.css +83 -5
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +7 -7
- package/payload/server/public/chat.html +13 -13
- package/payload/server/public/data.html +11 -11
- package/payload/server/public/graph.html +9 -9
- package/payload/server/public/index.html +14 -14
- package/payload/server/public/operator.html +14 -14
- package/payload/server/public/public.html +13 -13
- package/payload/server/public/routines.html +6 -6
- package/payload/server/public/skills.html +5 -5
- package/payload/server/public/tasks.html +6 -6
- package/payload/server/server.js +1584 -837
- package/payload/server/public/assets/admin-B-EGUWS4.js +0 -1
- package/payload/server/public/assets/agents-DPulfmrd.js +0 -1
- package/payload/server/public/assets/chat-DWLYAA70.js +0 -1
- package/payload/server/public/assets/chevron-left-DBMCTKlT.js +0 -1
- package/payload/server/public/assets/chevron-right-SBAr6zzc.js +0 -1
- package/payload/server/public/assets/clock-kkzt7Scf.js +0 -1
- package/payload/server/public/assets/data-CV8N9qcy.js +0 -1
- package/payload/server/public/assets/page-JJa2xdzf.js +0 -1
- package/payload/server/public/assets/useSubAccountSwitcher-DSW9vDi2.js +0 -9
- package/payload/server/public/assets/useSubAccountSwitcher-DyrH8DA8.css +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: platform-architecture
|
|
3
3
|
description: Use when grounding any documented-surface claim about what SiteDesk ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
|
|
4
|
-
content-hash: sha256:
|
|
4
|
+
content-hash: sha256:b15d0e491d957f75523ecf7056f8004bafcdf448767c0c3dd64b40a73926b58b
|
|
5
5
|
brand: sitedesk-code
|
|
6
6
|
product-name: SiteDesk
|
|
7
7
|
---
|
|
@@ -474,6 +474,32 @@ Skill content, plugin manifests, agent templates, and reference files reference
|
|
|
474
474
|
|
|
475
475
|
The runtime substitution happens at every read site that flows content into a system prompt or operator-visible UI: the admin agent's `plugin-read` tool (references + `PLUGIN.md`), the `skill-load` tool (SKILL.md by skill name — one-call resolver+reader, the canonical primitive for SKILL.md), the public agent's recursive plugin assembly, and `IDENTITY` / `SOUL` / `AGENTS` / `KNOWLEDGE` markdown reads. Missing or empty `productName` hard-fails — there is no fallback to a default brand string. See [.docs/agents.md](../../.docs/agents.md) § "Brand templating" for the full contract.
|
|
476
476
|
|
|
477
|
+
## Tool risk classes (for plugin authors)
|
|
478
|
+
|
|
479
|
+
Every tool a plugin declares in its `PLUGIN.md` `tools:` block carries three mandatory
|
|
480
|
+
fields. `publicAllowlist` and `adminAllowlist` say who may call the tool. `riskClass`
|
|
481
|
+
says what happens when they do, and takes one of four values:
|
|
482
|
+
|
|
483
|
+
- `read` changes no state anywhere. A remote lookup counts as `read`; reaching the
|
|
484
|
+
network is not by itself consequential.
|
|
485
|
+
- `write_local` mutates account files or the graph on this device and transmits nothing.
|
|
486
|
+
- `exec` runs an operating-system command, or code supplied by the caller or the operator.
|
|
487
|
+
- `external` transmits to, or changes state on, a system outside this device, or delivers
|
|
488
|
+
a message to a person.
|
|
489
|
+
|
|
490
|
+
Leaving the field out is a refusal-to-boot condition, not a silent default. The platform
|
|
491
|
+
refuses to start rather than treat an undeclared consequence as harmless, because a tool
|
|
492
|
+
that quietly reads as `read` escapes every check downstream of it.
|
|
493
|
+
|
|
494
|
+
Declaring the class does not yet gate anything. It gives any consumer one honest answer
|
|
495
|
+
to the question "is this tool consequential?" without maintaining a list of tool names,
|
|
496
|
+
which is what the checks in `platform/plugins/admin/hooks/` have had to do until now.
|
|
497
|
+
|
|
498
|
+
A publish-time gate flags declarations that contradict what the tool's own name says it
|
|
499
|
+
does, and a boot line reports the census across all four classes. The classification
|
|
500
|
+
rule, the exception mechanism, and the diagnostic commands are in
|
|
501
|
+
`.docs/tool-risk-classes.md`.
|
|
502
|
+
|
|
477
503
|
## MCP Plugin Observability (for plugin authors)
|
|
478
504
|
|
|
479
505
|
Every `console.error` line from a plugin's MCP server is captured to a per-server raw log. Capture is automatic and needs no import or call: the `mcp-spawn-tee` shim spawns every MCP server and mirrors its stderr to `mcp-<name>-<sessionId>.log` (or `mcp-<name>-nosession.log` for a spawn with no session). That file is keyed by session, which is the conversation, and that is where you grep. Prefix your diagnostics `[your-tool]` so a grep isolates one tool.
|
|
@@ -3078,8 +3104,11 @@ not-found|rm-error>`.
|
|
|
3078
3104
|
|
|
3079
3105
|
### Agents
|
|
3080
3106
|
|
|
3081
|
-
The Agents surface (`/agents`) is the admin view of the account's agents
|
|
3082
|
-
|
|
3107
|
+
The Agents surface (`/agents`) is the admin view of the account's agents. Each
|
|
3108
|
+
card is tagged by **origin**, which says who put the agent there and therefore
|
|
3109
|
+
what the operator may do with it. Origin is a different question to `kind`, which
|
|
3110
|
+
says what shape the agent is: a shipped specialist and a user-created one are
|
|
3111
|
+
both `kind: 'specialist'` and differ only by origin.
|
|
3083
3112
|
|
|
3084
3113
|
- **Public agents** — the per-agent directories under `<accountDir>/agents/<slug>/`,
|
|
3085
3114
|
each carrying a `config.json` (`displayName`, `status`, `model`,
|
|
@@ -3090,19 +3119,63 @@ kinds, each card tagged `Public` or `Specialist`:
|
|
|
3090
3119
|
to the public URL (computed client-side: an `admin.` host maps to the `public.`
|
|
3091
3120
|
host), plus a **delete** behind a confirm that runs the loud-fail `DELETE`
|
|
3092
3121
|
(graph projection cleanup precedes file removal).
|
|
3093
|
-
- **User-created specialists** — the agent files under
|
|
3122
|
+
- **User-created specialists** (`origin: 'specialist'`) — the agent files under
|
|
3094
3123
|
`<accountDir>/plugins/<plugin>/agents/<plugin>--<name>.md` (written by
|
|
3095
|
-
`agent-builder`).
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3124
|
+
`agent-builder`). The specialist modal shows the model, tools, the owning
|
|
3125
|
+
plugin's skills, and the system-prompt body. Specialists are authored, edited,
|
|
3126
|
+
and deleted through chat (`agent-builder` / `specialist-management`), so the
|
|
3127
|
+
specialist modal offers no delete or open-agent link.
|
|
3128
|
+
- **Shipped specialists** (`origin: 'shipped'`) — the union of
|
|
3129
|
+
`<accountDir>/specialists/agents/`, `<accountDir>/specialists/agents-disabled/`
|
|
3130
|
+
and `PLATFORM_ROOT/templates/specialists/agents/`, in that precedence order.
|
|
3131
|
+
`sidebar-artefacts.ts` reads the same union minus the quarantine directory, so
|
|
3132
|
+
a disabled agent still appears there through its bundled-template row. The
|
|
3133
|
+
route once walked only the account plugins tree, so the twelve
|
|
3134
|
+
highest-capability agents on the box were the ones the operator could not see.
|
|
3135
|
+
These are the only agents that can be disabled, and the route enforces that
|
|
3136
|
+
rather than relying on the UI to withhold the control.
|
|
3102
3137
|
|
|
3103
3138
|
The former "Public" flyout toggle in the account menu was removed; this page is
|
|
3104
3139
|
the sole agent surface.
|
|
3105
3140
|
|
|
3141
|
+
**Risk class.** Every classified card carries a green/amber/red badge derived
|
|
3142
|
+
from the agent's declared `tools:` line: green when every tool is `read`, amber
|
|
3143
|
+
when the worst is `write_local`, red on any `exec` or `external`, and red when
|
|
3144
|
+
any tool resolves to no class at all. Resolution has two sources, because
|
|
3145
|
+
frontmatter names tools in Claude Code's namespace: a plugin tool is a direct
|
|
3146
|
+
lookup in `ToolSurface.riskByTool`, which is keyed by the same
|
|
3147
|
+
canonical `mcp__plugin_<p>_<p>__<tool>` string frontmatter writes, and the
|
|
3148
|
+
eleven built-ins carry their own table in `server/lib/agent-risk.ts`. An empty
|
|
3149
|
+
tool list is red: no `tools:` line means the full surface, not none of it.
|
|
3150
|
+
|
|
3151
|
+
A public agent carries `risk: null` and shows no badge. Its tool surface
|
|
3152
|
+
resolves through `ADMIN_CORE_TOOLS` plus the plugin allowlists rather than a
|
|
3153
|
+
`tools:` line, and classifying its empty list would paint every public agent red
|
|
3154
|
+
on a premise nothing established.
|
|
3155
|
+
|
|
3156
|
+
The detail modal renders one chip per declared tool carrying **that tool's own**
|
|
3157
|
+
class, so a `read` tool inside a red agent still reads as harmless; a tool with
|
|
3158
|
+
no class is dashed and uncoloured rather than painted as if it were understood.
|
|
3159
|
+
Badge colours come from the shared semantic status set: `-solid` with white text
|
|
3160
|
+
for the badge, `-tint` with `-textOnTint` for a chip, never white.
|
|
3161
|
+
|
|
3162
|
+
**Disable.** `<accountDir>/specialists/agents/` is the live dispatchable set —
|
|
3163
|
+
`spawn-context.ts` reads it to build the spawn manifest — so disabling **moves**
|
|
3164
|
+
the agent file to `<accountDir>/specialists/agents-disabled/` and records the
|
|
3165
|
+
basename in a 0600 `<accountDir>/agents-disabled.json`. A flag alone would leave
|
|
3166
|
+
the agent running while the card claimed otherwise.
|
|
3167
|
+
|
|
3168
|
+
The file is moved, never deleted: a premium `--` agent and an operator-edited
|
|
3169
|
+
override each exist only in the account dir, so deleting one and later restoring
|
|
3170
|
+
from the bundled template would hand back a different agent than the operator
|
|
3171
|
+
switched off. A disabled agent stays listed, marked `Disabled`, because an agent
|
|
3172
|
+
that vanished from the surface that disabled it could never be switched back on.
|
|
3173
|
+
|
|
3174
|
+
`provision-account-dir.sh` re-reads the store after its core-specialist recopy
|
|
3175
|
+
and removes anything named in it, so an upgrade does not silently re-deliver a
|
|
3176
|
+
disabled agent. An unreadable store withholds nothing and says so: a corrupt
|
|
3177
|
+
file is not evidence that an agent was disabled.
|
|
3178
|
+
|
|
3106
3179
|
**Account scope.** The route (`server/routes/admin/agents.ts`) never infers an
|
|
3107
3180
|
account from device state. The two reads resolve from the caller's admin session
|
|
3108
3181
|
(`requireAdminSession` + the shared `accountDirForSession` in
|
|
@@ -3116,8 +3189,10 @@ explicit `?plugin=` parameter, not inferred from `--` in the slug.
|
|
|
3116
3189
|
|
|
3117
3190
|
| Route | Behaviour |
|
|
3118
3191
|
|-------|-----------|
|
|
3119
|
-
| `GET /api/admin/agents` | Session-scoped. Lists the session account's public `agents/*/` dirs (never the `admin` agent)
|
|
3120
|
-
| `GET /api/admin/agents/:slug` | Session-scoped. Without `?plugin=`, returns a public agent's config fields + four owned docs + a `present` map (a missing or unreadable doc is `''`/`present.<role>=false`, never a 500). With `?plugin=`,
|
|
3192
|
+
| `GET /api/admin/agents` | Session-scoped. Lists the session account's public `agents/*/` dirs (never the `admin` agent), its user-created specialists from `plugins/*/agents/*.md`, and its shipped specialists, each row tagged `kind` and `origin` and carrying `risk`, `worstTool`, `unresolved` and `disabled`. Returns `{agents, accountId, skipped, specialistsSkipped, shippedSkipped}`. 401 when the session maps to no account. |
|
|
3193
|
+
| `GET /api/admin/agents/:slug` | Session-scoped. Without `?plugin=` or `?origin=`, returns a public agent's config fields + four owned docs + a `present` map (a missing or unreadable doc is `''`/`present.<role>=false`, never a 500). With `?plugin=`, that plugin's user-created specialist; with `?origin=shipped`, the shipped one. Both specialist shapes add `{risk, worstTool, unresolved, byTool, disabled}`, where `byTool` is one class per declared tool. Selection is explicit rather than inferred, because a premium file and a user-created specialist under a plugin of the same name produce the same slug. Unknown 404s, 401 as above. |
|
|
3194
|
+
| `POST /api/admin/agents/:slug/disable?accountId=` | Shipped agents only, enforced here: a slug present in none of the three shipped directories 404s. Moves the file from `specialists/agents/` to `specialists/agents-disabled/` and records the basename in the 0600 store. Returns `{ok, moved}`; `moved:false` means the agent existed only as a bundled template, so nothing was there to move and the store entry is what stops the next provisioning run delivering it. The store is read strictly before anything moves, so an unreadable store 500s with the file untouched rather than rewriting the file whole from an empty set and dropping every other disabled agent. Same `accountId` contract as delete. |
|
|
3195
|
+
| `POST /api/admin/agents/:slug/enable?accountId=` | Covers both of disable's outcomes. Returns `{ok, restored}`: a quarantined file moves back (`restored:true`); a bundled-only agent has nothing to move, so clearing the store entry is the whole job (`restored:false`), which is what stops provisioning withholding it. Treating that second case as "nothing to restore" made disable a one-way door. 404 only when neither directory nor store knows the agent. |
|
|
3121
3196
|
| `DELETE /api/admin/agents/:slug?accountId=` | Public agents only, on the named validated account. Removes the dir after `deleteAgentProjection`; refuses the `admin` slug (403) and a missing/unknown `accountId` (400) with no write. Loud-fail: a graph-cleanup throw aborts the file removal. |
|
|
3122
3197
|
| `POST /api/admin/agents/:slug/project?accountId=` | Re-projects the named account's on-disk agent into the graph. Same `accountId` contract as delete. |
|
|
3123
3198
|
|
|
@@ -3144,6 +3219,35 @@ Agents page survived undiagnosed on a multi-account install.
|
|
|
3144
3219
|
- `op=delete accountId=<id8> slug=<…> outcome=<ok|failed>` with
|
|
3145
3220
|
`reason=<graph-cleanup-failed|rm-error>` on failure (files preserved on a
|
|
3146
3221
|
graph-cleanup throw).
|
|
3222
|
+
- `op=classify account=<id8> agent=<name> origin=<shipped|public|specialist>
|
|
3223
|
+
risk=<green|amber|red> worstTool=<tool> tools=<n> unresolved=<comma-list|none>
|
|
3224
|
+
disabled=<bool>` — one line per classified agent per listing, so it fires on
|
|
3225
|
+
every page load. `worstTool` makes a wrong class diagnosable without
|
|
3226
|
+
re-deriving it. **A non-empty `unresolved=` is the one to act on**: the agent
|
|
3227
|
+
is forced red, and the cause is that the built-in table or the registry
|
|
3228
|
+
mapping has drifted, not that the agent changed.
|
|
3229
|
+
- `op=risk-surface status=load-failed reason=<msg>` — the plugin registry did
|
|
3230
|
+
not parse, so every row is forced red and the listing returns
|
|
3231
|
+
`riskSurfaceFailed: true`. Without the forced red, every plugin tool would
|
|
3232
|
+
land in `unresolved` and a registry outage would read as agent drift across
|
|
3233
|
+
every card at once; without the response field, a red row would be
|
|
3234
|
+
indistinguishable from a genuinely dangerous agent, because nothing resolved
|
|
3235
|
+
so `unresolved` is empty and the per-tool explanation cannot fire.
|
|
3236
|
+
- `op=disabled-store status=parse-failed reason=<msg>` — the listing then treats
|
|
3237
|
+
every agent as enabled, which is what they actually are. A read surface shows
|
|
3238
|
+
the true dispatchable set rather than a comforting one.
|
|
3239
|
+
- `op=<disable|enable> accountId=<id8> slug=<…> outcome=<ok|failed>`, with
|
|
3240
|
+
`moved=<bool>` on disable and `reason=nothing-quarantined` on a failed enable.
|
|
3241
|
+
|
|
3242
|
+
**Disable is a no-event failure, so it needs a standing check.** If the store
|
|
3243
|
+
names an agent whose file is still in `specialists/agents/`, nothing throws, the
|
|
3244
|
+
card reads disabled, and the agent keeps being dispatched. The five-minute
|
|
3245
|
+
`account-dir-schema-reconcile` pass reports it as
|
|
3246
|
+
`[fs-reconcile] op=agent-parity account=<id8> disabled=<n> stillOnDisk=<list>`,
|
|
3247
|
+
fired only on disagreement. The per-cycle heartbeat is `agent-drift=0` on that
|
|
3248
|
+
pass's summary line, which is what makes the absence of a per-account line
|
|
3249
|
+
readable as "clean" rather than "never audited" — grep the summary line, not the
|
|
3250
|
+
per-account one, to confirm the check is running.
|
|
3147
3251
|
|
|
3148
3252
|
### Graph
|
|
3149
3253
|
|
|
@@ -3277,9 +3381,29 @@ the Web Share API and is disabled where unavailable. Signals:
|
|
|
3277
3381
|
`[data-ui] op=mount header=operator home=absent`, `op=select-enter`,
|
|
3278
3382
|
`op=select-exit reason=<cancel|action>`, `op=share supported=<bool> count=<n>`.
|
|
3279
3383
|
|
|
3384
|
+
**The dashboard has a surface ramp and a display type tier.** Every page sits on
|
|
3385
|
+
a page plane, cards rise onto a raised plane, and inputs and scroll regions
|
|
3386
|
+
recede onto an inset plane. Each brand chooses how the card separates: SiteDesk's
|
|
3387
|
+
card is a shade lighter than its page and casts no shadow, Real Agent's card and
|
|
3388
|
+
page are both white and a shadow does the work. The page header is an inverted
|
|
3389
|
+
band carrying the page title at the display size. Colours, radii, shadows and
|
|
3390
|
+
type weights all come from the brand's own tokens; nothing is hardcoded, and a
|
|
3391
|
+
token that resolves to nothing fails the build rather than painting invisibly.
|
|
3392
|
+
|
|
3393
|
+
**Folders open beside a rail on a wide screen.** At 1280px and above the folder
|
|
3394
|
+
view splits in two: a list of the account's folders on the left, the files on
|
|
3395
|
+
the right, with the folder you are in highlighted. Below that width the rail is
|
|
3396
|
+
hidden and the breadcrumb does the same job.
|
|
3397
|
+
|
|
3398
|
+
**Status colours are shared.** Success, warning, error and info each carry a
|
|
3399
|
+
solid fill, a pale tint and a text colour for that tint. Every pairing meets the
|
|
3400
|
+
WCAG AA contrast minimum, on every brand.
|
|
3401
|
+
|
|
3280
3402
|
**`/data` opens on the account's own folders, as cards.** With no `#path=` in
|
|
3281
3403
|
the URL the browser lands on **home**: the account's business folders (the
|
|
3282
|
-
entity folders of its trade, plus
|
|
3404
|
+
entity folders of its trade, plus documents, projects, contacts, the work it
|
|
3405
|
+
has produced for you, anything a client has sent in, and any websites it
|
|
3406
|
+
has published) shown
|
|
3283
3407
|
one per card, each listing the first 20 things inside it and a `+N more` row
|
|
3284
3408
|
past that. Tapping a card opens that folder in the ordinary list or grid. There
|
|
3285
3409
|
are never loose files at this level. Everything the platform and its plugins
|
|
@@ -9,6 +9,20 @@ Invoked by the admin agent directly.
|
|
|
9
9
|
|
|
10
10
|
This is the platform's release timeline, newest first. Each entry shows the date it shipped and the version it shipped in, so you can tell the operator how current their install is. To compare, read the installed version from `capabilities-here` and match it against the versions below. Keep answers high level and in plain English; this is a summary, not a full commit log.
|
|
11
11
|
|
|
12
|
+
## 2026-07-25 (0.1.508)
|
|
13
|
+
|
|
14
|
+
- SiteDesk's screens now use a single, cleaner typeface throughout, and the old olive-green colour is gone.
|
|
15
|
+
- You can now disable an agent from the Agents page, and each one shows its risk level.
|
|
16
|
+
- Dashboard sections appear as soon as each one loads, instead of the whole page waiting on the slowest one.
|
|
17
|
+
- Real Agent's icons and imagery were redrawn to match its colour palette.
|
|
18
|
+
|
|
19
|
+
## 2026-07-25 (0.1.507)
|
|
20
|
+
|
|
21
|
+
- Chat now updates the moment a message or permission prompt arrives, instead of checking every couple of seconds.
|
|
22
|
+
- The dashboard has a visual refresh: cards now fill in properly instead of looking transparent.
|
|
23
|
+
- The Data page has a new two-pane view with a folder rail on wider screens, and uploads/sites are now correctly grouped under Home.
|
|
24
|
+
- Every assistant tool now carries a declared risk level, checked automatically so it cannot silently drift.
|
|
25
|
+
|
|
12
26
|
## 2026-07-25 (0.1.506)
|
|
13
27
|
|
|
14
28
|
- Fixed a bug where the Agents page and a few other admin screens could resolve the wrong account.
|
|
@@ -5,12 +5,15 @@ tools:
|
|
|
5
5
|
- name: aeo-emit-jsonld
|
|
6
6
|
publicAllowlist: false
|
|
7
7
|
adminAllowlist: true
|
|
8
|
+
riskClass: write_local
|
|
8
9
|
- name: aeo-write-llms-txt
|
|
9
10
|
publicAllowlist: false
|
|
10
11
|
adminAllowlist: true
|
|
12
|
+
riskClass: write_local
|
|
11
13
|
- name: aeo-audit-page
|
|
12
14
|
publicAllowlist: false
|
|
13
15
|
adminAllowlist: true
|
|
16
|
+
riskClass: write_local
|
|
14
17
|
skills:
|
|
15
18
|
- skills/structured-answer/SKILL.md
|
|
16
19
|
always: false
|
|
@@ -5,57 +5,75 @@ tools:
|
|
|
5
5
|
- name: browser-render
|
|
6
6
|
publicAllowlist: false
|
|
7
7
|
adminAllowlist: true
|
|
8
|
+
riskClass: read
|
|
8
9
|
- name: browser-navigate
|
|
9
10
|
publicAllowlist: false
|
|
10
11
|
adminAllowlist: true
|
|
12
|
+
riskClass: read
|
|
11
13
|
- name: browser-snapshot
|
|
12
14
|
publicAllowlist: false
|
|
13
15
|
adminAllowlist: true
|
|
16
|
+
riskClass: read
|
|
14
17
|
- name: browser-click
|
|
15
18
|
publicAllowlist: false
|
|
16
19
|
adminAllowlist: true
|
|
20
|
+
riskClass: external
|
|
17
21
|
- name: browser-fill
|
|
18
22
|
publicAllowlist: false
|
|
19
23
|
adminAllowlist: true
|
|
24
|
+
riskClass: external
|
|
20
25
|
- name: browser-fill-form
|
|
21
26
|
publicAllowlist: false
|
|
22
27
|
adminAllowlist: true
|
|
28
|
+
riskClass: external
|
|
23
29
|
- name: browser-type
|
|
24
30
|
publicAllowlist: false
|
|
25
31
|
adminAllowlist: true
|
|
32
|
+
riskClass: external
|
|
26
33
|
- name: browser-press-key
|
|
27
34
|
publicAllowlist: false
|
|
28
35
|
adminAllowlist: true
|
|
36
|
+
riskClass: external
|
|
29
37
|
- name: browser-hover
|
|
30
38
|
publicAllowlist: false
|
|
31
39
|
adminAllowlist: true
|
|
40
|
+
riskClass: read
|
|
32
41
|
- name: browser-select-option
|
|
33
42
|
publicAllowlist: false
|
|
34
43
|
adminAllowlist: true
|
|
44
|
+
riskClass: external
|
|
35
45
|
- name: browser-wait-for
|
|
36
46
|
publicAllowlist: false
|
|
37
47
|
adminAllowlist: true
|
|
48
|
+
riskClass: read
|
|
38
49
|
- name: browser-handle-dialog
|
|
39
50
|
publicAllowlist: false
|
|
40
51
|
adminAllowlist: true
|
|
52
|
+
riskClass: external
|
|
41
53
|
- name: browser-evaluate
|
|
42
54
|
publicAllowlist: false
|
|
43
55
|
adminAllowlist: true
|
|
56
|
+
riskClass: exec
|
|
44
57
|
- name: browser-console-messages
|
|
45
58
|
publicAllowlist: false
|
|
46
59
|
adminAllowlist: true
|
|
60
|
+
riskClass: read
|
|
47
61
|
- name: browser-tabs
|
|
48
62
|
publicAllowlist: false
|
|
49
63
|
adminAllowlist: true
|
|
64
|
+
riskClass: read
|
|
50
65
|
- name: browser-pdf-save
|
|
51
66
|
publicAllowlist: false
|
|
52
67
|
adminAllowlist: true
|
|
68
|
+
riskClass: write_local
|
|
53
69
|
- name: browser-screenshot
|
|
54
70
|
publicAllowlist: false
|
|
55
71
|
adminAllowlist: true
|
|
72
|
+
riskClass: write_local
|
|
56
73
|
- name: browser-resize
|
|
57
74
|
publicAllowlist: false
|
|
58
75
|
adminAllowlist: true
|
|
76
|
+
riskClass: read
|
|
59
77
|
always: true
|
|
60
78
|
embed: false
|
|
61
79
|
metadata: {"platform":{}}
|
|
@@ -5,15 +5,19 @@ tools:
|
|
|
5
5
|
- name: connector-register
|
|
6
6
|
publicAllowlist: false
|
|
7
7
|
adminAllowlist: true
|
|
8
|
+
riskClass: write_local
|
|
8
9
|
- name: connector-call
|
|
9
10
|
publicAllowlist: false
|
|
10
11
|
adminAllowlist: true
|
|
12
|
+
riskClass: external
|
|
11
13
|
- name: connector-list
|
|
12
14
|
publicAllowlist: false
|
|
13
15
|
adminAllowlist: true
|
|
16
|
+
riskClass: read
|
|
14
17
|
- name: connector-deregister
|
|
15
18
|
publicAllowlist: false
|
|
16
19
|
adminAllowlist: true
|
|
20
|
+
riskClass: write_local
|
|
17
21
|
metadata: {"platform":{"optional":true,"pluginKey":"connector"}}
|
|
18
22
|
mcp:
|
|
19
23
|
command: node
|
|
@@ -5,30 +5,39 @@ tools:
|
|
|
5
5
|
- name: contact-create
|
|
6
6
|
publicAllowlist: false
|
|
7
7
|
adminAllowlist: false
|
|
8
|
+
riskClass: write_local
|
|
8
9
|
- name: contact-lookup
|
|
9
10
|
publicAllowlist: false
|
|
10
11
|
adminAllowlist: false
|
|
12
|
+
riskClass: read
|
|
11
13
|
- name: contact-update
|
|
12
14
|
publicAllowlist: false
|
|
13
15
|
adminAllowlist: false
|
|
16
|
+
riskClass: write_local
|
|
14
17
|
- name: contact-delete
|
|
15
18
|
publicAllowlist: false
|
|
16
19
|
adminAllowlist: false
|
|
20
|
+
riskClass: write_local
|
|
17
21
|
- name: contact-list
|
|
18
22
|
publicAllowlist: false
|
|
19
23
|
adminAllowlist: false
|
|
24
|
+
riskClass: read
|
|
20
25
|
- name: contact-export
|
|
21
26
|
publicAllowlist: false
|
|
22
27
|
adminAllowlist: false
|
|
28
|
+
riskClass: write_local
|
|
23
29
|
- name: contact-erase
|
|
24
30
|
publicAllowlist: false
|
|
25
31
|
adminAllowlist: false
|
|
32
|
+
riskClass: write_local
|
|
26
33
|
- name: group-create
|
|
27
34
|
publicAllowlist: false
|
|
28
35
|
adminAllowlist: false
|
|
36
|
+
riskClass: write_local
|
|
29
37
|
- name: group-manage
|
|
30
38
|
publicAllowlist: false
|
|
31
39
|
adminAllowlist: false
|
|
40
|
+
riskClass: write_local
|
|
32
41
|
always: false
|
|
33
42
|
embed: false
|
|
34
43
|
metadata: {"platform":{}}
|
|
@@ -151,8 +151,11 @@ not-found|rm-error>`.
|
|
|
151
151
|
|
|
152
152
|
### Agents
|
|
153
153
|
|
|
154
|
-
The Agents surface (`/agents`) is the admin view of the account's agents
|
|
155
|
-
|
|
154
|
+
The Agents surface (`/agents`) is the admin view of the account's agents. Each
|
|
155
|
+
card is tagged by **origin**, which says who put the agent there and therefore
|
|
156
|
+
what the operator may do with it. Origin is a different question to `kind`, which
|
|
157
|
+
says what shape the agent is: a shipped specialist and a user-created one are
|
|
158
|
+
both `kind: 'specialist'` and differ only by origin.
|
|
156
159
|
|
|
157
160
|
- **Public agents** — the per-agent directories under `<accountDir>/agents/<slug>/`,
|
|
158
161
|
each carrying a `config.json` (`displayName`, `status`, `model`,
|
|
@@ -163,19 +166,63 @@ kinds, each card tagged `Public` or `Specialist`:
|
|
|
163
166
|
to the public URL (computed client-side: an `admin.` host maps to the `public.`
|
|
164
167
|
host), plus a **delete** behind a confirm that runs the loud-fail `DELETE`
|
|
165
168
|
(graph projection cleanup precedes file removal).
|
|
166
|
-
- **User-created specialists** — the agent files under
|
|
169
|
+
- **User-created specialists** (`origin: 'specialist'`) — the agent files under
|
|
167
170
|
`<accountDir>/plugins/<plugin>/agents/<plugin>--<name>.md` (written by
|
|
168
|
-
`agent-builder`).
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
171
|
+
`agent-builder`). The specialist modal shows the model, tools, the owning
|
|
172
|
+
plugin's skills, and the system-prompt body. Specialists are authored, edited,
|
|
173
|
+
and deleted through chat (`agent-builder` / `specialist-management`), so the
|
|
174
|
+
specialist modal offers no delete or open-agent link.
|
|
175
|
+
- **Shipped specialists** (`origin: 'shipped'`) — the union of
|
|
176
|
+
`<accountDir>/specialists/agents/`, `<accountDir>/specialists/agents-disabled/`
|
|
177
|
+
and `PLATFORM_ROOT/templates/specialists/agents/`, in that precedence order.
|
|
178
|
+
`sidebar-artefacts.ts` reads the same union minus the quarantine directory, so
|
|
179
|
+
a disabled agent still appears there through its bundled-template row. The
|
|
180
|
+
route once walked only the account plugins tree, so the twelve
|
|
181
|
+
highest-capability agents on the box were the ones the operator could not see.
|
|
182
|
+
These are the only agents that can be disabled, and the route enforces that
|
|
183
|
+
rather than relying on the UI to withhold the control.
|
|
175
184
|
|
|
176
185
|
The former "Public" flyout toggle in the account menu was removed; this page is
|
|
177
186
|
the sole agent surface.
|
|
178
187
|
|
|
188
|
+
**Risk class.** Every classified card carries a green/amber/red badge derived
|
|
189
|
+
from the agent's declared `tools:` line: green when every tool is `read`, amber
|
|
190
|
+
when the worst is `write_local`, red on any `exec` or `external`, and red when
|
|
191
|
+
any tool resolves to no class at all. Resolution has two sources, because
|
|
192
|
+
frontmatter names tools in Claude Code's namespace: a plugin tool is a direct
|
|
193
|
+
lookup in `ToolSurface.riskByTool`, which is keyed by the same
|
|
194
|
+
canonical `mcp__plugin_<p>_<p>__<tool>` string frontmatter writes, and the
|
|
195
|
+
eleven built-ins carry their own table in `server/lib/agent-risk.ts`. An empty
|
|
196
|
+
tool list is red: no `tools:` line means the full surface, not none of it.
|
|
197
|
+
|
|
198
|
+
A public agent carries `risk: null` and shows no badge. Its tool surface
|
|
199
|
+
resolves through `ADMIN_CORE_TOOLS` plus the plugin allowlists rather than a
|
|
200
|
+
`tools:` line, and classifying its empty list would paint every public agent red
|
|
201
|
+
on a premise nothing established.
|
|
202
|
+
|
|
203
|
+
The detail modal renders one chip per declared tool carrying **that tool's own**
|
|
204
|
+
class, so a `read` tool inside a red agent still reads as harmless; a tool with
|
|
205
|
+
no class is dashed and uncoloured rather than painted as if it were understood.
|
|
206
|
+
Badge colours come from the shared semantic status set: `-solid` with white text
|
|
207
|
+
for the badge, `-tint` with `-textOnTint` for a chip, never white.
|
|
208
|
+
|
|
209
|
+
**Disable.** `<accountDir>/specialists/agents/` is the live dispatchable set —
|
|
210
|
+
`spawn-context.ts` reads it to build the spawn manifest — so disabling **moves**
|
|
211
|
+
the agent file to `<accountDir>/specialists/agents-disabled/` and records the
|
|
212
|
+
basename in a 0600 `<accountDir>/agents-disabled.json`. A flag alone would leave
|
|
213
|
+
the agent running while the card claimed otherwise.
|
|
214
|
+
|
|
215
|
+
The file is moved, never deleted: a premium `--` agent and an operator-edited
|
|
216
|
+
override each exist only in the account dir, so deleting one and later restoring
|
|
217
|
+
from the bundled template would hand back a different agent than the operator
|
|
218
|
+
switched off. A disabled agent stays listed, marked `Disabled`, because an agent
|
|
219
|
+
that vanished from the surface that disabled it could never be switched back on.
|
|
220
|
+
|
|
221
|
+
`provision-account-dir.sh` re-reads the store after its core-specialist recopy
|
|
222
|
+
and removes anything named in it, so an upgrade does not silently re-deliver a
|
|
223
|
+
disabled agent. An unreadable store withholds nothing and says so: a corrupt
|
|
224
|
+
file is not evidence that an agent was disabled.
|
|
225
|
+
|
|
179
226
|
**Account scope.** The route (`server/routes/admin/agents.ts`) never infers an
|
|
180
227
|
account from device state. The two reads resolve from the caller's admin session
|
|
181
228
|
(`requireAdminSession` + the shared `accountDirForSession` in
|
|
@@ -189,8 +236,10 @@ explicit `?plugin=` parameter, not inferred from `--` in the slug.
|
|
|
189
236
|
|
|
190
237
|
| Route | Behaviour |
|
|
191
238
|
|-------|-----------|
|
|
192
|
-
| `GET /api/admin/agents` | Session-scoped. Lists the session account's public `agents/*/` dirs (never the `admin` agent)
|
|
193
|
-
| `GET /api/admin/agents/:slug` | Session-scoped. Without `?plugin=`, returns a public agent's config fields + four owned docs + a `present` map (a missing or unreadable doc is `''`/`present.<role>=false`, never a 500). With `?plugin=`,
|
|
239
|
+
| `GET /api/admin/agents` | Session-scoped. Lists the session account's public `agents/*/` dirs (never the `admin` agent), its user-created specialists from `plugins/*/agents/*.md`, and its shipped specialists, each row tagged `kind` and `origin` and carrying `risk`, `worstTool`, `unresolved` and `disabled`. Returns `{agents, accountId, skipped, specialistsSkipped, shippedSkipped}`. 401 when the session maps to no account. |
|
|
240
|
+
| `GET /api/admin/agents/:slug` | Session-scoped. Without `?plugin=` or `?origin=`, returns a public agent's config fields + four owned docs + a `present` map (a missing or unreadable doc is `''`/`present.<role>=false`, never a 500). With `?plugin=`, that plugin's user-created specialist; with `?origin=shipped`, the shipped one. Both specialist shapes add `{risk, worstTool, unresolved, byTool, disabled}`, where `byTool` is one class per declared tool. Selection is explicit rather than inferred, because a premium file and a user-created specialist under a plugin of the same name produce the same slug. Unknown 404s, 401 as above. |
|
|
241
|
+
| `POST /api/admin/agents/:slug/disable?accountId=` | Shipped agents only, enforced here: a slug present in none of the three shipped directories 404s. Moves the file from `specialists/agents/` to `specialists/agents-disabled/` and records the basename in the 0600 store. Returns `{ok, moved}`; `moved:false` means the agent existed only as a bundled template, so nothing was there to move and the store entry is what stops the next provisioning run delivering it. The store is read strictly before anything moves, so an unreadable store 500s with the file untouched rather than rewriting the file whole from an empty set and dropping every other disabled agent. Same `accountId` contract as delete. |
|
|
242
|
+
| `POST /api/admin/agents/:slug/enable?accountId=` | Covers both of disable's outcomes. Returns `{ok, restored}`: a quarantined file moves back (`restored:true`); a bundled-only agent has nothing to move, so clearing the store entry is the whole job (`restored:false`), which is what stops provisioning withholding it. Treating that second case as "nothing to restore" made disable a one-way door. 404 only when neither directory nor store knows the agent. |
|
|
194
243
|
| `DELETE /api/admin/agents/:slug?accountId=` | Public agents only, on the named validated account. Removes the dir after `deleteAgentProjection`; refuses the `admin` slug (403) and a missing/unknown `accountId` (400) with no write. Loud-fail: a graph-cleanup throw aborts the file removal. |
|
|
195
244
|
| `POST /api/admin/agents/:slug/project?accountId=` | Re-projects the named account's on-disk agent into the graph. Same `accountId` contract as delete. |
|
|
196
245
|
|
|
@@ -217,6 +266,35 @@ Agents page survived undiagnosed on a multi-account install.
|
|
|
217
266
|
- `op=delete accountId=<id8> slug=<…> outcome=<ok|failed>` with
|
|
218
267
|
`reason=<graph-cleanup-failed|rm-error>` on failure (files preserved on a
|
|
219
268
|
graph-cleanup throw).
|
|
269
|
+
- `op=classify account=<id8> agent=<name> origin=<shipped|public|specialist>
|
|
270
|
+
risk=<green|amber|red> worstTool=<tool> tools=<n> unresolved=<comma-list|none>
|
|
271
|
+
disabled=<bool>` — one line per classified agent per listing, so it fires on
|
|
272
|
+
every page load. `worstTool` makes a wrong class diagnosable without
|
|
273
|
+
re-deriving it. **A non-empty `unresolved=` is the one to act on**: the agent
|
|
274
|
+
is forced red, and the cause is that the built-in table or the registry
|
|
275
|
+
mapping has drifted, not that the agent changed.
|
|
276
|
+
- `op=risk-surface status=load-failed reason=<msg>` — the plugin registry did
|
|
277
|
+
not parse, so every row is forced red and the listing returns
|
|
278
|
+
`riskSurfaceFailed: true`. Without the forced red, every plugin tool would
|
|
279
|
+
land in `unresolved` and a registry outage would read as agent drift across
|
|
280
|
+
every card at once; without the response field, a red row would be
|
|
281
|
+
indistinguishable from a genuinely dangerous agent, because nothing resolved
|
|
282
|
+
so `unresolved` is empty and the per-tool explanation cannot fire.
|
|
283
|
+
- `op=disabled-store status=parse-failed reason=<msg>` — the listing then treats
|
|
284
|
+
every agent as enabled, which is what they actually are. A read surface shows
|
|
285
|
+
the true dispatchable set rather than a comforting one.
|
|
286
|
+
- `op=<disable|enable> accountId=<id8> slug=<…> outcome=<ok|failed>`, with
|
|
287
|
+
`moved=<bool>` on disable and `reason=nothing-quarantined` on a failed enable.
|
|
288
|
+
|
|
289
|
+
**Disable is a no-event failure, so it needs a standing check.** If the store
|
|
290
|
+
names an agent whose file is still in `specialists/agents/`, nothing throws, the
|
|
291
|
+
card reads disabled, and the agent keeps being dispatched. The five-minute
|
|
292
|
+
`account-dir-schema-reconcile` pass reports it as
|
|
293
|
+
`[fs-reconcile] op=agent-parity account=<id8> disabled=<n> stillOnDisk=<list>`,
|
|
294
|
+
fired only on disagreement. The per-cycle heartbeat is `agent-drift=0` on that
|
|
295
|
+
pass's summary line, which is what makes the absence of a per-account line
|
|
296
|
+
readable as "clean" rather than "never audited" — grep the summary line, not the
|
|
297
|
+
per-account one, to confirm the check is running.
|
|
220
298
|
|
|
221
299
|
### Graph
|
|
222
300
|
|
|
@@ -350,9 +428,29 @@ the Web Share API and is disabled where unavailable. Signals:
|
|
|
350
428
|
`[data-ui] op=mount header=operator home=absent`, `op=select-enter`,
|
|
351
429
|
`op=select-exit reason=<cancel|action>`, `op=share supported=<bool> count=<n>`.
|
|
352
430
|
|
|
431
|
+
**The dashboard has a surface ramp and a display type tier.** Every page sits on
|
|
432
|
+
a page plane, cards rise onto a raised plane, and inputs and scroll regions
|
|
433
|
+
recede onto an inset plane. Each brand chooses how the card separates: SiteDesk's
|
|
434
|
+
card is a shade lighter than its page and casts no shadow, Real Agent's card and
|
|
435
|
+
page are both white and a shadow does the work. The page header is an inverted
|
|
436
|
+
band carrying the page title at the display size. Colours, radii, shadows and
|
|
437
|
+
type weights all come from the brand's own tokens; nothing is hardcoded, and a
|
|
438
|
+
token that resolves to nothing fails the build rather than painting invisibly.
|
|
439
|
+
|
|
440
|
+
**Folders open beside a rail on a wide screen.** At 1280px and above the folder
|
|
441
|
+
view splits in two: a list of the account's folders on the left, the files on
|
|
442
|
+
the right, with the folder you are in highlighted. Below that width the rail is
|
|
443
|
+
hidden and the breadcrumb does the same job.
|
|
444
|
+
|
|
445
|
+
**Status colours are shared.** Success, warning, error and info each carry a
|
|
446
|
+
solid fill, a pale tint and a text colour for that tint. Every pairing meets the
|
|
447
|
+
WCAG AA contrast minimum, on every brand.
|
|
448
|
+
|
|
353
449
|
**`/data` opens on the account's own folders, as cards.** With no `#path=` in
|
|
354
450
|
the URL the browser lands on **home**: the account's business folders (the
|
|
355
|
-
entity folders of its trade, plus
|
|
451
|
+
entity folders of its trade, plus documents, projects, contacts, the work it
|
|
452
|
+
has produced for you, anything a client has sent in, and any websites it
|
|
453
|
+
has published) shown
|
|
356
454
|
one per card, each listing the first 20 things inside it and a `+N more` row
|
|
357
455
|
past that. Tapping a card opens that folder in the ordinary list or grid. There
|
|
358
456
|
are never loose files at this level. Everything the platform and its plugins
|
|
@@ -150,6 +150,32 @@ Skill content, plugin manifests, agent templates, and reference files reference
|
|
|
150
150
|
|
|
151
151
|
The runtime substitution happens at every read site that flows content into a system prompt or operator-visible UI: the admin agent's `plugin-read` tool (references + `PLUGIN.md`), the `skill-load` tool (SKILL.md by skill name — one-call resolver+reader, the canonical primitive for SKILL.md), the public agent's recursive plugin assembly, and `IDENTITY` / `SOUL` / `AGENTS` / `KNOWLEDGE` markdown reads. Missing or empty `productName` hard-fails — there is no fallback to a default brand string. See [.docs/agents.md](../../.docs/agents.md) § "Brand templating" for the full contract.
|
|
152
152
|
|
|
153
|
+
## Tool risk classes (for plugin authors)
|
|
154
|
+
|
|
155
|
+
Every tool a plugin declares in its `PLUGIN.md` `tools:` block carries three mandatory
|
|
156
|
+
fields. `publicAllowlist` and `adminAllowlist` say who may call the tool. `riskClass`
|
|
157
|
+
says what happens when they do, and takes one of four values:
|
|
158
|
+
|
|
159
|
+
- `read` changes no state anywhere. A remote lookup counts as `read`; reaching the
|
|
160
|
+
network is not by itself consequential.
|
|
161
|
+
- `write_local` mutates account files or the graph on this device and transmits nothing.
|
|
162
|
+
- `exec` runs an operating-system command, or code supplied by the caller or the operator.
|
|
163
|
+
- `external` transmits to, or changes state on, a system outside this device, or delivers
|
|
164
|
+
a message to a person.
|
|
165
|
+
|
|
166
|
+
Leaving the field out is a refusal-to-boot condition, not a silent default. The platform
|
|
167
|
+
refuses to start rather than treat an undeclared consequence as harmless, because a tool
|
|
168
|
+
that quietly reads as `read` escapes every check downstream of it.
|
|
169
|
+
|
|
170
|
+
Declaring the class does not yet gate anything. It gives any consumer one honest answer
|
|
171
|
+
to the question "is this tool consequential?" without maintaining a list of tool names,
|
|
172
|
+
which is what the checks in `platform/plugins/admin/hooks/` have had to do until now.
|
|
173
|
+
|
|
174
|
+
A publish-time gate flags declarations that contradict what the tool's own name says it
|
|
175
|
+
does, and a boot line reports the census across all four classes. The classification
|
|
176
|
+
rule, the exception mechanism, and the diagnostic commands are in
|
|
177
|
+
`.docs/tool-risk-classes.md`.
|
|
178
|
+
|
|
153
179
|
## MCP Plugin Observability (for plugin authors)
|
|
154
180
|
|
|
155
181
|
Every `console.error` line from a plugin's MCP server is captured to a per-server raw log. Capture is automatic and needs no import or call: the `mcp-spawn-tee` shim spawns every MCP server and mirrors its stderr to `mcp-<name>-<sessionId>.log` (or `mcp-<name>-nosession.log` for a spawn with no session). That file is keyed by session, which is the conversation, and that is where you grep. Prefix your diagnostics `[your-tool]` so a grep isolates one tool.
|