@bevel-software/platform-core-backend 0.34.0 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/agent-guide/access-control.md +146 -300
  2. package/agent-guide/conventions.md +23 -25
  3. package/agent-guide/directory-structure.md +72 -120
  4. package/agent-guide/drafts-and-change-requests.md +92 -0
  5. package/agent-guide/for-git-users.md +47 -0
  6. package/agent-guide/glossary.md +36 -0
  7. package/agent-guide/how-agents-find-things.md +49 -0
  8. package/agent-guide/html-views.md +16 -11
  9. package/agent-guide/new-knowledge-base.md +9 -8
  10. package/agent-guide/skills.md +67 -42
  11. package/agent-guide/start-here.md +65 -0
  12. package/agent-guide/tool-manuals.md +132 -154
  13. package/agent-guide/tools.md +67 -0
  14. package/agent-guide/where-a-new-file-goes.md +45 -28
  15. package/dist/core/create-core-server.js +1 -1
  16. package/dist/core/create-core-server.js.map +1 -1
  17. package/dist/core/create-core-services.d.ts +6 -0
  18. package/dist/core/create-core-services.d.ts.map +1 -1
  19. package/dist/core/create-core-services.js +7 -4
  20. package/dist/core/create-core-services.js.map +1 -1
  21. package/dist/modules/access/access-control.interface.d.ts +22 -0
  22. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  23. package/dist/modules/access/access-control.service.d.ts +3 -1
  24. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  25. package/dist/modules/access/access-control.service.js +75 -0
  26. package/dist/modules/access/access-control.service.js.map +1 -1
  27. package/dist/modules/access/access.routes.d.ts.map +1 -1
  28. package/dist/modules/access/access.routes.js +23 -4
  29. package/dist/modules/access/access.routes.js.map +1 -1
  30. package/dist/modules/access/index.d.ts +1 -1
  31. package/dist/modules/access/index.d.ts.map +1 -1
  32. package/dist/modules/agent-guide/agent-guide.d.ts +47 -7
  33. package/dist/modules/agent-guide/agent-guide.d.ts.map +1 -1
  34. package/dist/modules/agent-guide/agent-guide.js +103 -13
  35. package/dist/modules/agent-guide/agent-guide.js.map +1 -1
  36. package/dist/modules/agent-guide/agent-guide.tools.d.ts +6 -2
  37. package/dist/modules/agent-guide/agent-guide.tools.d.ts.map +1 -1
  38. package/dist/modules/agent-guide/agent-guide.tools.js +45 -31
  39. package/dist/modules/agent-guide/agent-guide.tools.js.map +1 -1
  40. package/dist/modules/agent-guide/guide-budget.d.ts +48 -0
  41. package/dist/modules/agent-guide/guide-budget.d.ts.map +1 -0
  42. package/dist/modules/agent-guide/guide-budget.js +67 -0
  43. package/dist/modules/agent-guide/guide-budget.js.map +1 -0
  44. package/dist/modules/agent-guide/index.d.ts +2 -1
  45. package/dist/modules/agent-guide/index.d.ts.map +1 -1
  46. package/dist/modules/agent-guide/index.js +2 -1
  47. package/dist/modules/agent-guide/index.js.map +1 -1
  48. package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
  49. package/dist/modules/agent-instructions/compose.js +4 -0
  50. package/dist/modules/agent-instructions/compose.js.map +1 -1
  51. package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -1
  52. package/dist/modules/agent-instructions/shared-file-rules.js +10 -8
  53. package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -1
  54. package/dist/modules/code-mode/code-mode.tool.js +2 -2
  55. package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
  56. package/dist/modules/connection-probe/connection-probe.service.d.ts +0 -12
  57. package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
  58. package/dist/modules/connection-probe/connection-probe.service.js +1 -38
  59. package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
  60. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  61. package/dist/modules/mcp/mcp.service.js +6 -35
  62. package/dist/modules/mcp/mcp.service.js.map +1 -1
  63. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts +11 -1
  64. package/dist/modules/secrets-vault/db-secrets-vault.service.d.ts.map +1 -1
  65. package/dist/modules/secrets-vault/db-secrets-vault.service.js +159 -24
  66. package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
  67. package/dist/modules/secrets-vault/index.d.ts +1 -1
  68. package/dist/modules/secrets-vault/index.d.ts.map +1 -1
  69. package/dist/modules/secrets-vault/index.js +1 -1
  70. package/dist/modules/secrets-vault/index.js.map +1 -1
  71. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts +53 -11
  72. package/dist/modules/secrets-vault/secrets-vault.contract.d.ts.map +1 -1
  73. package/dist/modules/secrets-vault/secrets-vault.contract.js +27 -0
  74. package/dist/modules/secrets-vault/secrets-vault.contract.js.map +1 -1
  75. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  76. package/dist/modules/secrets-vault/secrets-vault.routes.js +85 -39
  77. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  78. package/dist/modules/settings/oidc-check.d.ts.map +1 -1
  79. package/dist/modules/settings/oidc-check.js +5 -17
  80. package/dist/modules/settings/oidc-check.js.map +1 -1
  81. package/dist/modules/skills/skills.tools.js +1 -1
  82. package/dist/modules/skills/skills.tools.js.map +1 -1
  83. package/dist/modules/tool-helpers/tool-def.js +1 -1
  84. package/dist/modules/tool-helpers/tool-def.js.map +1 -1
  85. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +7 -1
  86. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
  87. package/dist/modules/tool-manuals/mcp-json-discovery.js +69 -38
  88. package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
  89. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +27 -0
  90. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
  91. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +12 -8
  92. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  93. package/dist/modules/tool-manuals/tool-manuals.service.js +115 -44
  94. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  95. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +8 -2
  96. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  97. package/dist/modules/tool-manuals/tool-manuals.tools.js +31 -10
  98. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  99. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  100. package/dist/modules/workflow/agent-tools/workflow.tools.js +22 -18
  101. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  102. package/dist/modules/workspace/agent-access.gate.d.ts +1 -1
  103. package/dist/modules/workspace/agent-access.gate.d.ts.map +1 -1
  104. package/dist/modules/workspace/agent-access.gate.js +1 -1
  105. package/dist/modules/workspace/agent-access.gate.js.map +1 -1
  106. package/dist/modules/workspace/workspace.tools.d.ts +3 -2
  107. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  108. package/dist/modules/workspace/workspace.tools.js +71 -21
  109. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  110. package/dist/shared/oauth-token-error.d.ts +23 -0
  111. package/dist/shared/oauth-token-error.d.ts.map +1 -0
  112. package/dist/shared/oauth-token-error.js +47 -0
  113. package/dist/shared/oauth-token-error.js.map +1 -0
  114. package/dist/shared/read-capped.d.ts +19 -0
  115. package/dist/shared/read-capped.d.ts.map +1 -0
  116. package/dist/shared/read-capped.js +45 -0
  117. package/dist/shared/read-capped.js.map +1 -0
  118. package/package.json +4 -3
  119. package/src/__tests__/hexis-catalog.ts +144 -0
  120. package/src/__tests__/real-disk.ts +21 -0
  121. package/src/__tests__/tokens.ts +19 -0
  122. package/src/__tests__/tool-manuals.ts +32 -0
  123. package/src/__tests__/tool-wiring.ts +25 -0
  124. package/src/core/__tests__/oauth-sign-in-real-file.test.ts +179 -0
  125. package/src/core/create-core-server.ts +1 -1
  126. package/src/core/create-core-services.ts +13 -3
  127. package/src/modules/access/__tests__/access.routes.prospective.test.ts +52 -12
  128. package/src/modules/access/__tests__/holder-changes.test.ts +127 -0
  129. package/src/modules/access/access-control.interface.ts +26 -0
  130. package/src/modules/access/access-control.service.ts +92 -0
  131. package/src/modules/access/access.routes.ts +23 -4
  132. package/src/modules/access/index.ts +1 -1
  133. package/src/modules/agent-guide/__tests__/agent-guide.test.ts +79 -25
  134. package/src/modules/agent-guide/__tests__/agent-guide.tools.test.ts +38 -11
  135. package/src/modules/agent-guide/__tests__/guide-budget.test.ts +95 -0
  136. package/src/modules/agent-guide/__tests__/guide-eval.test.ts +182 -0
  137. package/src/modules/agent-guide/__tests__/guide-matches-code.test.ts +263 -0
  138. package/src/modules/agent-guide/__tests__/tool-surface.json +514 -0
  139. package/src/modules/agent-guide/__tests__/tool-surface.test.ts +75 -0
  140. package/src/modules/agent-guide/agent-guide.tools.ts +49 -31
  141. package/src/modules/agent-guide/agent-guide.ts +120 -13
  142. package/src/modules/agent-guide/guide-budget.ts +73 -0
  143. package/src/modules/agent-guide/index.ts +12 -0
  144. package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +4 -4
  145. package/src/modules/agent-instructions/compose.ts +4 -0
  146. package/src/modules/agent-instructions/shared-file-rules.ts +10 -8
  147. package/src/modules/code-mode/code-mode.tool.ts +2 -2
  148. package/src/modules/connection-probe/__tests__/connection-probe.service.test.ts +5 -24
  149. package/src/modules/connection-probe/connection-probe.service.ts +1 -34
  150. package/src/modules/mcp/mcp.service.ts +5 -35
  151. package/src/modules/secrets-vault/__tests__/db-secrets-vault.oauth.test.ts +430 -3
  152. package/src/modules/secrets-vault/__tests__/oauth-live-declaration.route.test.ts +218 -0
  153. package/src/modules/secrets-vault/__tests__/oauth-return-to.route.test.ts +39 -6
  154. package/src/modules/secrets-vault/db-secrets-vault.service.ts +170 -27
  155. package/src/modules/secrets-vault/index.ts +1 -1
  156. package/src/modules/secrets-vault/secrets-vault.contract.ts +61 -11
  157. package/src/modules/secrets-vault/secrets-vault.routes.ts +91 -39
  158. package/src/modules/settings/oidc-check.ts +5 -18
  159. package/src/modules/skills/skills.tools.ts +1 -1
  160. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +19 -11
  161. package/src/modules/tool-helpers/__tests__/route-tool-schemas.catalog.test.ts +38 -0
  162. package/src/modules/tool-helpers/tool-def.ts +1 -1
  163. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +197 -11
  164. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +96 -5
  165. package/src/modules/tool-manuals/mcp-json-discovery.ts +73 -34
  166. package/src/modules/tool-manuals/tool-manuals.contract.ts +22 -0
  167. package/src/modules/tool-manuals/tool-manuals.service.ts +138 -46
  168. package/src/modules/tool-manuals/tool-manuals.tools.ts +53 -14
  169. package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +6 -113
  170. package/src/modules/workflow/agent-tools/workflow.tools.ts +22 -18
  171. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +8 -10
  172. package/src/modules/workspace/__tests__/workspace.tools.test.ts +156 -49
  173. package/src/modules/workspace/agent-access.gate.ts +1 -1
  174. package/src/modules/workspace/workspace.tools.ts +74 -21
  175. package/src/shared/__tests__/read-capped.test.ts +68 -0
  176. package/src/shared/oauth-token-error.ts +46 -0
  177. package/src/shared/read-capped.ts +40 -0
  178. package/agent-guide/finding-things.md +0 -7
  179. package/agent-guide/introduction.md +0 -27
@@ -5,141 +5,93 @@
5
5
  ├── {{knowledgeBaseDir}}/ ← the knowledge itself; organise it however suits you
6
6
  ├── {{skillsDir}}/ ← shared skills, organised by who owns them
7
7
  ├── {{pluginsDir}}/ ← one folder per plugin: its tools, and links to skills
8
- ├── roles.yaml ← identity → role mapping (Admin-only edits)
9
- └── access.md ← repo-root access-control rules
8
+ ├── roles.yaml ← who holds which role
9
+ ├── groups.yaml ← who is in which group
10
+ └── access.md ← the access rules at the root
10
11
  ```
11
12
 
12
- (The three root names above are this deployment's own — a deployment may
13
- rename them in its setup screen, and this guide is rendered with the names in
14
- effect each time it is composed.)
13
+ (The three root names are this deployment's own: a deployment may rename them
14
+ in its setup screen, and this guide names the folders in effect.)
15
15
 
16
16
  Tool paths are workspace-relative, and the workspace root holds this
17
- repository as the `{{kbDirName}}/` folder (this deployment's own name for its
18
- checkout): a file in it is `{{kbDirName}}/{{knowledgeBaseDir}}/Foo.md`. Write
19
- the prefix where you can — it is the path every tool reports back — but a
20
- path without it is PLACED under `{{kbDirName}}/` rather than refused, so
21
- `{{knowledgeBaseDir}}/Foo.md` names that same file, and so does the
22
- root-anchored `/{{kbDirName}}/{{knowledgeBaseDir}}/Foo.md` the app's Copy
23
- path gives you. Nothing you send can land beside the repository,
24
- where git would never see it. `.` or `..` segments, backslashes and every other
25
- absolute path are refused.
26
-
27
- Only those three folders are structural. `{{skillsDir}}/` holds shared skills at any
28
- depth — the folder that holds a `SKILL.md` is the skill, and everything above
29
- it is ownership (`{{skillsDir}}/<scope>/…/<skill>/SKILL.md`, with an `access.md` in
17
+ repository as the `{{kbDirName}}/` folder: a file in it is
18
+ `{{kbDirName}}/{{knowledgeBaseDir}}/Foo.md`. Write the prefix where you can, as
19
+ every tool reports paths with it; a path without it is placed under
20
+ `{{kbDirName}}/`, so `{{knowledgeBaseDir}}/Foo.md` and the app's Copy path
21
+ `/{{kbDirName}}/{{knowledgeBaseDir}}/Foo.md` name the same file. A leading
22
+ `./` and doubled slashes are tidied away; `..`, any other `.` segment,
23
+ backslashes and other absolute paths are refused.
24
+
25
+ Only those three folders are structural, and `Data/`, `Agents/` and
26
+ `Pipelines/` are reserved names on every deployment. Under
27
+ `{{knowledgeBaseDir}}/`, subfolders, naming and whether a topic is one file or
28
+ twenty are yours to decide. `{{skillsDir}}/` holds shared skills at any depth:
29
+ the folder that holds a `SKILL.md` is the skill, and the folders above it are
30
+ ownership (`{{skillsDir}}/<scope>/…/<skill>/SKILL.md`, with an `access.md` in
30
31
  any scope folder that needs its own rules). `{{pluginsDir}}/` has a layout the
31
32
  platform reads:
32
33
 
33
34
  ```text
34
- {{pluginsDir}}/<Plugin>/plugin.json the manifest (Agent Plugins) — what makes the folder a plugin; its `name` is the plugin's identity
35
+ {{pluginsDir}}/<Plugin>/plugin.json the manifest: what makes the folder a plugin; its `name` is the plugin's identity
35
36
  {{pluginsDir}}/<Plugin>/skills/<skill>/SKILL.md a skill that lives inside the plugin
36
- {{pluginsDir}}/<Plugin>/mcp.json MCP servers (authoritative)
37
- {{pluginsDir}}/<Plugin>/software.bevel.hexis/tools/ `.tool` manuals
38
- {{pluginsDir}}/<Plugin>/access.md who can read/write the plugin
39
- {{pluginsDir}}/personal-<user-id>/… one per person: private
37
+ {{pluginsDir}}/<Plugin>/mcp.json its MCP servers
38
+ {{pluginsDir}}/<Plugin>/software.bevel.hexis/tools/ its `.tool` manuals
39
+ {{pluginsDir}}/<Plugin>/access.md who can read and write the plugin
40
+ {{pluginsDir}}/personal-<id>/… one per person: private
40
41
  ```
41
42
 
42
43
  **The manifest's `name` is the plugin.** It is a kebab-case identifier
43
- (`sales-team`), and it is what every grant spells (`plugin/sales-team/read`),
44
- what the URLs and the catalog key on, and what the compiled marketplace
45
- publishes the plugin as. `displayName` is what people see it called ("Sales
46
- Team"); absent, the folder name is shown. Rename a plugin from its page in the
47
- app: an identifier change rewrites every grant that names it, in one commit —
48
- editing `name` by hand leaves those grants pointing at a plugin that no longer
49
- exists.
44
+ (`sales-team`): what every grant spells (`plugin/sales-team/read`) and what the
45
+ app and the marketplace key on. `displayName` is what people see ("Sales
46
+ Team"); without one, the `name` is shown. Rename a plugin from its page in the
47
+ app, which rewrites every grant naming it in one commit. Editing `name` by hand
48
+ leaves those grants pointing at a plugin that no longer exists.
50
49
 
51
- **A plugin LINKS shared skills rather than containing them.** Its manifest
52
- lists skill paths under `extensions["software.bevel.hexis"].skills` — each
53
- entry is one skill folder or a folder of skills under `{{skillsDir}}/`:
50
+ **A plugin links shared skills rather than containing them.** Its manifest
51
+ lists skill folders, or folders of skills, under `{{skillsDir}}/`:
54
52
 
55
53
  ```json
56
54
  { "extensions": { "software.bevel.hexis": { "skills": ["{{skillsDir}}/Engineering/deploy", "{{skillsDir}}/Sales"] } } }
57
55
  ```
58
56
 
59
- One skill, stored once, can be listed by many plugins. A plugin's effective
60
- skills are the ones inside its folder plus everything its links resolve to.
61
- Do not edit that list by hand: linking is done from the plugin's page in the
62
- app, because it is two edits at once — the manifest entry AND a grant on the
63
- skill (see *Access control* below). A manifest entry without the grant lists
64
- a skill the plugin's members cannot read; the app shows such a link as
65
- needing setup and offers Repair.
66
-
67
- **Ownership decides who may read a skill, never the plugin.** A shared
68
- skill's readability comes from the `access.md` rules on its own folder and
69
- the scopes above it. A plugin that links a skill someone cannot read simply
70
- does not show it to them.
71
-
72
- **Symlinks are not supported anywhere under `{{pluginsDir}}/`.** Access control
73
- resolves rules by path, and a symlink is a second path to the same content —
74
- the two can disagree about who may read what. The platform never creates
75
- them and ignores any it finds (they can only arrive via a direct git push).
76
-
77
- **A plugin follows the [Agent Plugins](https://agent-plugins.org) specification**
78
- (v1.0.0), so another conformant client can load one: it reads `plugin.json`, the
79
- skills under `skills/`, and the servers in `mcp.json`, and ignores everything
80
- else. Two things here are ours and sit outside that portable core. `access.md`
81
- stays at the plugin root because access resolution walks root → file, so the
82
- same rules one level down would govern only that subtree. And `http`/`inline` `.tool`
83
- manuals live under the reverse-DNS `software.bevel.hexis/` namespace, because
84
- the specification describes MCP servers only and has no way to express them.
85
-
86
- **MCP servers belong in `mcp.json` — do not write `.tool` files for them.**
87
- Each `mcpServers` key is the server's identity: it is the namespace its vault
88
- secrets bind to (`<name>_<VAR>`), so renaming a key unbinds every configured
89
- secret and sign-in. The portable entry carries only where the server is
90
- (`type`, `url`, literal headers). Anything this platform needs beyond that —
91
- auth headers carrying `${VAR}` vault references, `variables` declarations,
92
- a `description`, or `local: true` for a server only reachable from a user's
93
- machine — goes in `plugin.json` under
94
- `extensions["software.bevel.hexis"].mcpServers[<name>]`, which other clients
95
- ignore by design. A `type: "stdio"` entry (a command run on the user's own
96
- machine) is always local: the hosted endpoint never spawns it; the local
97
- `hexis-mcp` server fetches the plugin's files to a local directory and runs it
98
- per the Agent Plugins runtime contract (`PLUGIN_ROOT`/`PLUGIN_DATA`, `./`
99
- commands contained to the plugin). A stdio server SHOULD exit when its stdin
100
- reaches EOF — the client also terminates it on shutdown, but a server that
101
- ignores EOF outlives crashes as an orphan whose working directory blocks the
102
- plugin folder from ever refreshing.
103
-
104
- **Secrets are never written into a plugin's portable files.** The specification
105
- defines no portable credential mechanism on purpose: authorization and
106
- credential storage are the client's business, header and `env` values are
107
- "visible package data", and a client must not expand anything except
108
- `${PLUGIN_ROOT}` and `${PLUGIN_DATA}`. So the Secrets Vault IS this platform's
109
- answer to that — and `mcp.json` carries only where a server is, never a
110
- `${VAR}` reference to how to authenticate with it. Those live in `plugin.json`
111
- under `extensions["software.bevel.hexis"].mcpServers[<name>]`, which is ours
112
- to interpret and which other clients ignore by design.
113
-
114
- **Plugin folders are made through the platform, not by writing files.** A
115
- folder is a plugin exactly when it carries a `plugin.json` (the platform
116
- writes one into every legacy plugin folder at startup), and it is LISTED only
117
- when it also carries an `access.md` — a bare directory under `{{pluginsDir}}/` is
118
- neither. Plugins may sit at any depth under `{{pluginsDir}}/`; a folder that holds
119
- plugins deeper down is a grouping folder, not a plugin. A new plugin needs an
120
- `access.md` naming who runs it, and the write gate refuses a plain write
121
- into an unused name there — so do not try to create a plugin by writing a
122
- skill into `{{pluginsDir}}/<new-name>/…`; it will be denied. Use the two tools
123
- instead:
124
-
125
- - `my_plugin` — your user's personal plugin, holding their own skills and
126
- tools, created on first use: `{{pluginsDir}}/personal-<id>/`. Readable only
127
- by its owner — not even admins — and never listed as a plugin. Their personal
128
- skills go under its `skills/`, each in its own folder with a `SKILL.md`;
129
- write there with the file tools. A note or any other document goes under
130
- `{{knowledgeBaseDir}}/` instead — see **Where a new file goes**.
131
- - `create_plugin` — a shared plugin, named, optionally inside a grouping
132
- folder under `{{pluginsDir}}/` (`parent`). The caller runs it; others join
133
- through the app or are granted in its `access.md`.
134
-
135
- The app's **New plugin** button and `POST /api/plugins` do the same. A skill
136
- moves from a personal plugin into a shared plugin by moving its folder.
137
-
138
- Everything under `{{knowledgeBaseDir}}/` is yours to arrange. Subfolders, naming,
139
- whether a topic is one file or twenty — all of it is a judgement call about
140
- what the next reader needs, not a rule the platform enforces.
141
-
142
- A deployment may reserve further root folders of its own — `Data/`, `Agents/`
143
- and `Pipelines/` scaffold an agentic execution layer in some installations.
144
- They are not part of this template and are not created here; where they exist,
145
- each carries its own `README.md` describing what belongs in it.
57
+ A plugin's skills are the ones in its own `skills/` folder plus everything its
58
+ links resolve to, and one skill can be linked by many plugins. Do not edit that
59
+ list by hand: link from the plugin's page in the app, which also grants the
60
+ plugin's members read on the skill (see `access-control`). A link without that
61
+ grant is repaired automatically when someone who may write the plugin opens its
62
+ page; one that cannot be repaired that way shows as needing setup. Who may read
63
+ a skill is decided by the skill's own folders, never by the plugin: a plugin
64
+ linking a skill someone cannot read simply does not show it to them.
65
+
66
+ **MCP servers go in `mcp.json`, not in `.tool` files.** Each `mcpServers` key
67
+ is the server's identity and the namespace its secrets bind to, so renaming a
68
+ key unbinds every configured secret and sign-in. `mcp.json` says only where the
69
+ server is (`type`, `url`, plain headers). Everything else the platform needs
70
+ (headers carrying `${VAR}` secrets, `variables`, a `description`, or
71
+ `local: true` for a server reachable only from a user's machine) goes in
72
+ `plugin.json` under `extensions["software.bevel.hexis"].mcpServers[<name>]`.
73
+ Secrets never go in `mcp.json`: plugins follow the
74
+ [Agent Plugins](https://agent-plugins.org) specification (v1.0.0), which other
75
+ clients load, and it has no place for credentials. A `type: "stdio"` server
76
+ runs on the user's machine through the local `hexis-mcp` server, never on the
77
+ hosted endpoint; it should exit when its standard input closes.
78
+
79
+ **Make plugins with the tools, not by writing files.** A folder is a plugin
80
+ when it has a `plugin.json` (or the read-only `plugin.bundle.json` format), and
81
+ it is listed only when it also has an `access.md`. Writing files into a new
82
+ name under `{{pluginsDir}}/` makes neither: on a protected branch it is
83
+ refused, and on a draft it makes a folder that is not a plugin. Use:
84
+
85
+ - `my_plugin`: the user's personal plugin, `{{pluginsDir}}/personal-<id>/`,
86
+ created on first use. Only its owner can read it, admins included, and it is
87
+ never listed as a plugin. Their own skills go in its `skills/` folder, each in
88
+ a folder with a `SKILL.md`. A note or any other document goes under
89
+ `{{knowledgeBaseDir}}/` instead (see `where-a-new-file-goes`).
90
+ - `create_plugin`: a shared plugin, named, optionally inside a grouping folder
91
+ (`parent`). The caller runs it; others join through the app or are granted
92
+ in its `access.md`.
93
+
94
+ The app's **New plugin** button does the same. A skill moves from a personal
95
+ plugin to a shared one by moving its folder. Symlinks are ignored anywhere
96
+ under `{{pluginsDir}}/`: access is resolved by path, and a second path to the
97
+ same file could disagree about who may read it.
@@ -0,0 +1,92 @@
1
+ ## Drafts and change requests
2
+
3
+ An agent drafts, proposes and syncs; people approve and apply, in the app.
4
+
5
+ - **Start a draft** with `create_branch`: `name` is the new draft and `branch`
6
+ the branch it forks from, usually `{{defaultBranch}}`:
7
+ `create_branch({ body: { name: "paul-brandt/pricing-page", branch: "{{defaultBranch}}" } })`.
8
+ Name it `<you>/<topic>`, where `<you>` is the person's email before the
9
+ `@`, in lower case, with each run of other characters than letters and
10
+ digits as one `-`: `paul.brandt@` gives `paul-brandt/`. Only that prefix
11
+ makes the draft theirs, so they can delete it later. Ask for the email if
12
+ you do not know it. A draft is shared at once, and the file tools then take
13
+ it as `branch`. In the app, the person sees it through its change request
14
+ once you open one; until then the app shows them `{{defaultBranch}}`.
15
+ - **Saving shares.** Every write is committed as the person and shared,
16
+ moments after the call returns; there is no separate push. `branch_status`
17
+ lists writes still landing. `share_current_branch` is only for sharing
18
+ again after a refused push, and `commit_change` only for a file a tool left
19
+ uncommitted (`unzip` does).
20
+ - **Propose** with `open_change_request`
21
+ (`sourceBranch` = the draft, `targetBranch` = the protected branch, a
22
+ `title`), then give the person the request's `url`. It first brings the
23
+ latest target into the draft. The answer lists `approvals` (who must approve
24
+ each file) and `mergeBlockedReasons`. Reviewing, approving and merging happen
25
+ in the app; someone who may approve every file it changes may merge their own
26
+ request there.
27
+ - **Edit** an open request with `update_change_request`: `description`
28
+ replaces the text whole, `appendDescription` adds a paragraph. Allowed for its
29
+ author and anyone who may merge it.
30
+ - **Know where a draft stands** with `branch_status`: ahead or behind, whether
31
+ the shared draft moved (`behindRemote`), writes still landing, the protected
32
+ branch it was forked from, and `changedPaths`, the files a request would
33
+ carry. That list holds only files you may read; `withheldPaths` counts the
34
+ others (changed or still landing), so above zero the list is not the whole
35
+ request. Ask it before proposing.
36
+ - **Sync** a draft with `update_branch` on the draft, `from` = the request's
37
+ target. It is allowed while the request is open. Without `from` it brings in
38
+ the draft's own shared state, for when it moved while your workspace held
39
+ commits (`behindRemote`). `merge_branch` (`source` = the target, `target` =
40
+ the draft) does the same without conflict handling.
41
+ - **`merge_branch` into a protected branch** merges only what you could write
42
+ there directly, never a change to `roles.yaml`, and refuses a draft whose
43
+ open request targets that branch: ask the person to review it in the app.
44
+ - **Read history** with `list_file_changes` (a file's commits: who, when,
45
+ subject), `read_file_at` (the file at a commit, given as the full sha, or on
46
+ another branch) and `compare_file` (the diff of a commit, or between two
47
+ branches). To put an earlier version back, read it with `read_file_at` and
48
+ write it with `write_file`.
49
+
50
+ ### Settling a conflict
51
+
52
+ When `update_branch` answers `conflicts`, it has left the draft exactly as it
53
+ was. For each conflicted path it carries the file at the merge base, on your
54
+ side (`ours`) and on theirs (`theirs`), and the working copy with git's
55
+ markers. A side is null when it has no file, or when `truncated` names it
56
+ (too large, or not text): then read `ours` with `read_file`, `theirs` with
57
+ `read_file_at` at `from` (the branch name), and the base with `read_file_at` at
58
+ the answer's `mergeBase`. A side that is not text cannot be read that way:
59
+ settle that file whole with `choose`. A conflicted file you may not read on one
60
+ of the branches is only counted (`withheldConflicts`): ask a person who can read
61
+ it to update the draft in the app.
62
+
63
+ Decide each file (keep both sides' information unless one is clearly stale;
64
+ `list_file_changes` says who changed what and why), then call `update_branch`
65
+ again with the same `from` and `resolutions`, one per conflicted path: `content`,
66
+ the resolved text, or `choose` (`ours`, `theirs`, `delete`). A resolution is a
67
+ write on your draft: you must be able to read the path (or the folder it lands
68
+ in). It merges, commits as you and shares. If sharing is refused, or the shared
69
+ draft moved meanwhile, the answer is the saved-locally error: the merge is on
70
+ your draft but not shared, and `branch_status` shows it ahead. Do not report it as shared; the next save
71
+ or `share_current_branch` carries it. You never have a half-done merge to
72
+ finish.
73
+
74
+ ### When a draft goes away
75
+
76
+ - **After a merge** the draft is deleted on its own, unless another change
77
+ request is still open from or into it. Start a new draft for the next change.
78
+ A draft whose open request turns out to propose nothing (its changes are
79
+ already on the target) is removed the same way when that request is closed,
80
+ and so is the draft of a request deleted in the app.
81
+ - **`delete_branch`** removes a draft for everyone. Only its author (the
82
+ `<you>/…` prefix above, or your own `suggestions/…` bundle) or an Admin may.
83
+ Preview with `dryRun: true`: it changes nothing and says whether the delete
84
+ would go through, how many commits are not on `{{defaultBranch}}`, and the
85
+ last commit. A draft holding such commits is refused unless you pass
86
+ `discardUnmerged: true`, only when the person wants that work gone. An open
87
+ request from or into it that proposes nothing is closed by the delete; one
88
+ that still proposes something refuses it, with a link: its author can
89
+ withdraw it, or an Admin can decline it, in the app. A request you cannot see
90
+ is not named, even in a preview, so a delete can still be refused after a
91
+ clean preview. Hand the person the `lastCommit` it answers, so the draft can
92
+ be restored.
@@ -0,0 +1,47 @@
1
+ ## If you know Git
2
+
3
+ The platform keeps its files in a Git repository, but the people you work with
4
+ never touch Git, and neither do you: you have no git command. Use the
5
+ platform's own words. What carries over, and what does not:
6
+
7
+ | | Git and a code host | Here |
8
+ |---|---|---|
9
+ | Branches | Anyone branches anything | The default branch, `{{defaultBranch}}`, and any other protected branch are live. Work in progress goes on a draft branch named `<you>/<topic>` (see `drafts-and-change-requests`) |
10
+ | Commits | `git commit`, then `git push` | Every save is committed and shared as the person, moments after the call returns. No separate push |
11
+ | Pull requests | Opened and merged on the code host | A change request: you open it (`open_change_request`), a person reviews, approves and merges it in the app. No agent tool approves or merges one |
12
+ | Who approves | Code owners, branch rules | The owners and writers of each file the request changes |
13
+ | Permissions | The whole repository, read or write | Per folder and per file: `access.md` files say who may read, write, download or own. What you cannot read you cannot see or change, on any branch |
14
+ | Layout | Free | Free under `{{knowledgeBaseDir}}/`; `{{skillsDir}}/` and `{{pluginsDir}}/` have a layout the platform reads |
15
+ | Skills and tools | Files like any other | Live only once on `{{defaultBranch}}`; on a draft they wait for review |
16
+
17
+ **On your draft you can do anything to what you can read.** Create, edit, move
18
+ or delete any file you can read (a few platform files aside, see
19
+ `working-with-files`). The draft is shared at once, so anyone who may read a
20
+ file can open it there, but nothing is live until the draft is merged. Merging
21
+ is where the owners of the files approve, so a delete on your draft deletes
22
+ for everyone only once they accept it.
23
+
24
+ **On a protected branch, write access is checked per file.** If you may write
25
+ every file a change touches, it lands directly; ask the person first. If not,
26
+ the write is refused, and the refusal says how to propose it instead.
27
+
28
+ **An example.** A team keeps its specs in `{{knowledgeBaseDir}}/Products/Search/`,
29
+ whose `access.md` is:
30
+
31
+ ```yaml
32
+ ---
33
+ owner:
34
+ - role/Admin
35
+ ---
36
+ read:
37
+ - everyone
38
+ write:
39
+ - group/Search Team
40
+ ```
41
+
42
+ The part above the second `---` says who may change this `access.md`; the
43
+ part below it is the folder's rules. Everyone signed in can read the specs.
44
+ Members of the group `Search Team` change them directly on `{{defaultBranch}}`.
45
+ Anyone else drafts the change on a branch and opens a change request, which a
46
+ member of `Search Team` approves. A folder inside it can narrow or widen this
47
+ with an `access.md` of its own. The full rules are in `access-control`.
@@ -0,0 +1,36 @@
1
+ ## Words used here
2
+
3
+ - **Knowledge base**: this organisation's documents, skills and tools in one
4
+ repository, with access set per folder.
5
+ - **Default branch** (`{{defaultBranch}}`): what is live. Other protected
6
+ branches may exist; `list_branches` marks each with `isProtected`.
7
+ - **Draft**: any branch that is not protected. Work waits there for review,
8
+ visible to whoever opens the draft.
9
+ - **Change request**: the request to merge a draft into a protected branch.
10
+ - **Plugin**: a package: a folder under `{{pluginsDir}}/` with a `plugin.json`.
11
+ It holds or links skills and declares tools, and its `access.md` says who
12
+ uses it. Each person also has a private one (`my_plugin`).
13
+ - **MCP server**: a connection to another service, declared in a plugin's
14
+ `mcp.json`. Its tools appear in the tool list as `<server>_<tool>`.
15
+ - **Tool**: one action an agent can call: one of the platform's own
16
+ (`read_file`), one from a plugin's MCP server, or one from a tool manual.
17
+ - **Tool manual**: a `.tool` file in a plugin that describes HTTP tools, for a
18
+ service without an MCP server.
19
+ - **Skill**: a folder with a `SKILL.md`: instructions an agent loads
20
+ (`get_skill`) when the skill's description fits the task.
21
+ - **Secret**: a credential a tool needs, kept in the platform's Secrets Vault,
22
+ never in a file or in the chat. A *shared* (admin) secret is set once, by
23
+ someone who may edit the tool, for everyone who uses it. A *personal* (user)
24
+ secret is set by each person for themselves.
25
+ - **Sign-in** (OAuth): a personal secret filled by signing in to the service
26
+ instead of pasting a key.
27
+ - **Connect page**: where each person sets their personal secrets and signs in
28
+ to tools.
29
+ - **Role**: a capability the platform defines, such as `Admin`, with the people
30
+ who hold it, in `roles.yaml`. Roles are pre-set: never create one.
31
+ - **Group**: a named set of people, such as a team or a project, in
32
+ `groups.yaml` or synced from the organisation's directory. Groups are what
33
+ `access.md` files usually grant.
34
+ - **`access.md`**: a folder's rules: who may `read`, `write`, `download` or be
35
+ `owner` of what is in it. A folder inherits from the folders above it, and
36
+ the nearest rule that names you decides.
@@ -0,0 +1,49 @@
1
+ ## How your agent finds things
2
+
3
+ Use this to know what you already have, and to explain it to the person you
4
+ work with when they ask "does the agent read everything?".
5
+
6
+ **When an agent connects, it has three things and no more:**
7
+
8
+ 1. **The server instructions**: a short platform note, the rules every file
9
+ tool shares, and this knowledge base's own description, which an admin
10
+ writes in `mcp-description.md` at the repository root (on the app's
11
+ external agent page). Some clients put them in the system prompt; a client
12
+ that drops them gets one line and the description's first paragraph at the
13
+ top of `start_session`, `grep`, `list_files` and `read_file` instead.
14
+ 2. **The tool list**: each tool's name, description and arguments.
15
+ 3. **The skills' names and descriptions**, as prompts and through
16
+ `list_skills`.
17
+
18
+ **Everything else is fetched when a task needs it**: `grep` for the key terms,
19
+ `list_files` to see a folder, `read_file` for a page, `get_skill` for a skill's
20
+ instructions, and this guide one section at a time. No agent reads the whole
21
+ knowledge base, and nothing makes it look: the instructions ask it to search
22
+ before it answers about the organisation, and the rest is up to the text below.
23
+
24
+ **What brings the right context is text the agent already has:**
25
+
26
+ - **A skill's description** decides when the skill is loaded. Write it as
27
+ "Use when …", in the words people use for the task.
28
+ - **Links between pages** (relative markdown links) lead one read to the next.
29
+ A page that links the pages explaining it is found together with them.
30
+ - **`mcp-description.md`** says what this knowledge base holds, where, and
31
+ when to look. Its first paragraph is the part every client sees.
32
+
33
+ **On the user's own machine**, the one text an agent always reads in a
34
+ repository is that repository's `AGENTS.md` or `CLAUDE.md`. A few lines there
35
+ make it look here first. Offer this, filled in:
36
+
37
+ ```markdown
38
+ ## Organisation knowledge
39
+ Before you plan, design or write about <product or team>, search the
40
+ <organisation> knowledge base (MCP server `<server name>`): grep for the key
41
+ terms and read what matches. <What> lives in `{{knowledgeBaseDir}}/<Folder>/`.
42
+ Prefer it over memory, and say when it is silent.
43
+ ```
44
+
45
+ **This knowledge base's own conventions** go in an `AGENTS.md` at its root,
46
+ written by its people: `read_file` on `AGENTS.md` returns that file first, then
47
+ the start of this guide. A `CLAUDE.md` at the root is theirs too. Follow both, and never
48
+ copy this guide into either: the platform serves it. Only the root file is read
49
+ this way: an `AGENTS.md` deeper in a folder is an ordinary page.
@@ -3,7 +3,13 @@
3
3
  An `.html` (or `.htm`) file in the knowledge base is a live page: the app shows
4
4
  it in a sandboxed frame, scripts and all. Write one when a reader is better
5
5
  served by something interactive — a dashboard, a filterable table, a chart —
6
- than by a markdown document.
6
+ than by a markdown document. Plain HTML with inline CSS and JavaScript is
7
+ enough; no skill or template is needed.
8
+
9
+ **Data written into the page is governed by the page's access.** A tool call
10
+ answers each viewer only what they may read; bytes in the file reach everyone
11
+ who may read the page, so never copy in data from a source fewer people may
12
+ read.
7
13
 
8
14
  ### The frame
9
15
 
@@ -15,6 +21,10 @@ than by a markdown document.
15
21
  `<object>`, `<embed>`, `<base>` and `<meta>` are removed. Write CSS in a
16
22
  `<style>` element and code in an inline `<script>`. An image is a `data:`
17
23
  URL.
24
+ - **The body is the page.** The app shows the sanitised contents of `<body>`
25
+ in a document of its own: `<style>` and inline `<script>` in `<head>` are
26
+ kept, moved to the start of the body; everything else in `<head>` and the
27
+ attributes on `<html>` and `<body>` are ignored.
18
28
  - **Scripts run as ES modules**, in source order, after the document is
19
29
  parsed: `document.getElementById` works without waiting for
20
30
  `DOMContentLoaded`, top-level `await` works, and a top-level declaration is
@@ -86,7 +96,7 @@ assume it is there:
86
96
  ```js
87
97
  const tools = window.bevel?.tools;
88
98
  if (tools) {
89
- const file = await tools.callTool('read_file', { body: { branch: 'main', path: '{{knowledgeBaseDir}}/Foo.md' } });
99
+ const file = await tools.callTool('read_file', { body: { branch: '{{defaultBranch}}', path: '{{knowledgeBaseDir}}/Foo.md' } });
90
100
  // …show it
91
101
  } else {
92
102
  // …render without it
@@ -131,7 +141,7 @@ the app; the page never sees it.
131
141
  const input = document.getElementById('briefing');
132
142
  input.addEventListener('change', async () => {
133
143
  const answer = await window.bevel.tools.uploadFile(input.files[0], {
134
- branch: 'main',
144
+ branch: '{{defaultBranch}}',
135
145
  destination: '{{knowledgeBaseDir}}/Campaigns/Easter',
136
146
  });
137
147
  // answer.files[0].path is where it landed
@@ -216,15 +226,10 @@ no build step and no framework:
216
226
  <head>
217
227
  <style>
218
228
  body { font: 14px system-ui, sans-serif; margin: 16px; }
219
- header { display: flex; align-items: center; gap: 12px; min-height: 40px; }
220
- #status { color: #555; }
229
+ .board { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
230
+ .column { min-height: 320px; border: 1px solid #ddd; padding: 8px; }
231
+ .column li { min-height: 20px; padding: 6px; list-style: none; }
221
232
  #status.error { color: #b00020; }
222
- .board { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
223
- .column { min-height: 320px; border: 1px solid #ddd; border-radius: 6px; padding: 8px; }
224
- .column ul { list-style: none; margin: 0; padding: 0; }
225
- .column li { min-height: 20px; padding: 6px; margin-bottom: 6px; border-radius: 4px; background: #f4f4f4; }
226
- .column li.placeholder { background: #ececec; }
227
- .count { color: #777; font-weight: normal; }
228
233
  </style>
229
234
  </head>
230
235
  <body>
@@ -1,21 +1,22 @@
1
1
  ## A new knowledge base
2
2
 
3
3
  When `start_session` answers with a `firstRun` note, this knowledge base is
4
- new: `{{kbDirName}}/{{knowledgeBaseDir}}/` has no pages yet, the starter guide
5
- aside. The person has most likely just connected you, and you are the quickest way to
6
- fill it. The note stops coming once real pages exist.
4
+ new: `{{kbDirName}}/{{knowledgeBaseDir}}/` holds nothing but its starter guide
5
+ and any starter pages nobody has changed yet. The person has most likely just
6
+ connected you, and you are the quickest way to fill it. The note stops coming
7
+ once real pages exist.
7
8
 
8
9
  - **Offer once per conversation, in a sentence or two.** For example: "Your
9
- Hexis knowledge base is connected and almost empty. Want me to draft a few
10
- pages about your company — what you do, your customers, your products, your
11
- glossary? Give me your website or tell me a bit, and I'll write them for you
12
- to review."
10
+ knowledge base is connected and almost empty. Want me to draft a few pages
11
+ about your company (what you do, your customers, your products, your
12
+ glossary)? Give me your website or tell me a bit, and I'll write them for
13
+ you to review."
13
14
  - **Never in the way.** If they asked for something else, do that first and
14
15
  make the offer in one line at the end. If they decline, drop it.
15
16
  - **Draft from what they give you.** Read the website they name, or work from
16
17
  what they tell you, and ask when something essential is missing rather than
17
18
  inventing it. Good first pages: About the company, Customers, Products,
18
- Glossary, How we work. Say where each fact came from (see Conventions).
19
+ Glossary, How we work. Say where each fact came from (see `conventions`).
19
20
  - **Write them with the normal tools** (`write_file`, `write_files`) as
20
21
  markdown under `{{kbDirName}}/{{knowledgeBaseDir}}/`, one page per subject.
21
22
  Leave the starter guide as it is. A write you are refused says whether you