@kaiba-cloud/orbital 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -0
- package/dist/index.js +10580 -6562
- package/dist/mod.json +1 -1
- package/dist/orbital.js +14928 -26392
- package/dist/runner.js +15068 -17088
- package/package.json +19 -18
- package/schemas/telemetry-snapshot.schema.json +17 -1
- package/skills/orbital-plugin-builder/SKILL.md +16 -14
- package/skills/orbital-plugin-builder/references/host-api.md +35 -1
- package/skills/orbital-plugin-builder/references/manifest.md +14 -14
- package/skills/orbital-plugin-builder/references/ports.md +137 -31
- package/skills/orbital-plugin-builder/references/recipes.md +133 -11
package/package.json
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaiba-cloud/orbital",
|
|
3
3
|
"description": "orbital: the control plane lite for agents on any harness: the node, its runner, its terminal UI, its protocol and its Claude Code mod, in one command line",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.3",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/ai-kaiba/atom.git",
|
|
10
|
-
"directory": "packages/orbital"
|
|
11
|
-
},
|
|
12
7
|
"engines": {
|
|
13
8
|
"node": ">=22.18"
|
|
14
9
|
},
|
|
@@ -16,14 +11,22 @@
|
|
|
16
11
|
"orbital": "dist/orbital.js"
|
|
17
12
|
},
|
|
18
13
|
"exports": {
|
|
19
|
-
".": "./
|
|
20
|
-
"./
|
|
14
|
+
".": "./src/index.ts",
|
|
15
|
+
"./protocol": "./src/protocol/index.ts",
|
|
16
|
+
"./sdk": "./src/sdk/index.ts",
|
|
17
|
+
"./tui": "./src/tui/index.tsx",
|
|
18
|
+
"./*": "./src/*.ts"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "node scripts/build.mjs",
|
|
22
|
+
"check-types": "tsc -p tsconfig.json",
|
|
23
|
+
"schemas": "node scripts/schemas.ts",
|
|
24
|
+
"test": "vitest run"
|
|
21
25
|
},
|
|
22
26
|
"dependencies": {
|
|
23
27
|
"@anthropic-ai/claude-agent-sdk": "^0.3.295",
|
|
24
28
|
"@anthropic-ai/sandbox-runtime": "0.0.76",
|
|
25
|
-
"@mariozechner/pi-ai": "0.73.1"
|
|
26
|
-
"@mariozechner/pi-coding-agent": "0.73.1"
|
|
29
|
+
"@mariozechner/pi-ai": "0.73.1"
|
|
27
30
|
},
|
|
28
31
|
"devDependencies": {
|
|
29
32
|
"@a2a-js/sdk": "1.3.0",
|
|
@@ -44,12 +47,10 @@
|
|
|
44
47
|
"schemas"
|
|
45
48
|
],
|
|
46
49
|
"publishConfig": {
|
|
47
|
-
"access": "public"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"schemas": "node scripts/schemas.ts",
|
|
53
|
-
"test": "vitest run"
|
|
50
|
+
"access": "public",
|
|
51
|
+
"exports": {
|
|
52
|
+
".": "./dist/index.js",
|
|
53
|
+
"./package.json": "./package.json"
|
|
54
|
+
}
|
|
54
55
|
}
|
|
55
|
-
}
|
|
56
|
+
}
|
|
@@ -214,6 +214,20 @@
|
|
|
214
214
|
],
|
|
215
215
|
"additionalProperties": false
|
|
216
216
|
}
|
|
217
|
+
},
|
|
218
|
+
"problems": {
|
|
219
|
+
"default": [],
|
|
220
|
+
"type": "array",
|
|
221
|
+
"items": {
|
|
222
|
+
"type": "string"
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
"retiring": {
|
|
226
|
+
"default": null,
|
|
227
|
+
"type": [
|
|
228
|
+
"string",
|
|
229
|
+
"null"
|
|
230
|
+
]
|
|
217
231
|
}
|
|
218
232
|
},
|
|
219
233
|
"required": [
|
|
@@ -221,7 +235,9 @@
|
|
|
221
235
|
"node",
|
|
222
236
|
"purpose",
|
|
223
237
|
"lead",
|
|
224
|
-
"members"
|
|
238
|
+
"members",
|
|
239
|
+
"problems",
|
|
240
|
+
"retiring"
|
|
225
241
|
],
|
|
226
242
|
"additionalProperties": false
|
|
227
243
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: orbital-plugin-builder
|
|
3
|
-
description: Use when building, changing or debugging any orbital plugin or port for the `orbital` CLI
|
|
3
|
+
description: Use when building, changing or debugging any orbital plugin or port for the `orbital` CLI — a channel (Slack, Discord, email), a store or workspace store, a harness, a panel, a policy engine, a telemetry sink or live dashboard (snapshot and events), a directory of agents, placements on other machines, session hooks, hosted tools, actions, workflows, a reasoner, a missions relay for cross-machine or LAN work, a plugin command or a background service — or when a plugin uses the host API, writes orbital-plugin.json or permissions, fails `orbital plugin check`, is refused at start, or must be built, added, tested or published to npm or a marketplace.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Build an orbital plugin
|
|
@@ -15,14 +15,14 @@ plain object for each port.
|
|
|
15
15
|
|---|---|
|
|
16
16
|
| [references/ports.md](references/ports.md) | Each port: what it is for, when the node calls it, the config, the interface, the remote methods, an example. |
|
|
17
17
|
| [references/host-api.md](references/host-api.md) | Each method of the host API: `submit`, `deliver`, `completeModel`, `missions`, `snapshot` and the others. |
|
|
18
|
-
| [references/manifest.md](references/manifest.md) | `orbital-plugin.json`, the `
|
|
19
|
-
| [references/recipes.md](references/recipes.md) | Whole plugins: a cross-machine missions relay with a directory and placements, a telemetry dashboard, a Slack channel, a policy engine. |
|
|
18
|
+
| [references/manifest.md](references/manifest.md) | `orbital-plugin.json`, the `orbital.yaml` entry, the manifest, permissions, secrets, `context.init`. |
|
|
19
|
+
| [references/recipes.md](references/recipes.md) | Whole plugins: a cross-machine missions relay with a directory and placements, a telemetry dashboard, a Slack channel, a policy engine, a gatekeeper for messages from the network. |
|
|
20
20
|
|
|
21
21
|
## Ports
|
|
22
22
|
|
|
23
23
|
| Port | Key | Use | Section |
|
|
24
24
|
|---|---|---|---|
|
|
25
|
-
| directory | `ports.directory` | agents, skills and blueprints that are not
|
|
25
|
+
| directory | `ports.directory` | agents, skills and blueprints that are not files of a harness folder | [directory](references/ports.md#directory) |
|
|
26
26
|
| placements | `ports.placements: { <kind>: Placement }` | run a session on another machine or service | [placements](references/ports.md#placements) |
|
|
27
27
|
| policy | `ports.policy` | allow, deny or ask before an action | [policy](references/ports.md#policy) |
|
|
28
28
|
| telemetry | `ports.telemetry` | usage, spans and node events, for a live dashboard | [telemetry](references/ports.md#telemetry) |
|
|
@@ -31,7 +31,7 @@ plain object for each port.
|
|
|
31
31
|
| channels | `ports.channels: { <id>: Channel }` | send messages and listen for them | [channels](references/ports.md#channels) |
|
|
32
32
|
| store | `ports.store` | session history and scratchpads off this machine | [store](references/ports.md#store) |
|
|
33
33
|
| workspace | `ports.workspace` | the workspace of a team or an agent off this machine | [workspace](references/ports.md#workspace) |
|
|
34
|
-
| harnesses | `ports.harnesses` |
|
|
34
|
+
| harnesses | `ports.harnesses` | the adapter of a harness: its folder, agent files and install, and the turns on its agent program | [harnesses](references/ports.md#harnesses) |
|
|
35
35
|
| panels | `ports.panels: { <id>: Panel }` | rows a surface shows, and the `missions` and `peers` panels | [panels](references/ports.md#panels) |
|
|
36
36
|
| actions | `ports.actions` | run the action types of the ontology | [actions](references/ports.md#actions) |
|
|
37
37
|
| workflows | `ports.workflows` | run workflow files on an engine | [workflows](references/ports.md#workflows) |
|
|
@@ -88,7 +88,7 @@ export function plugin(context: PluginContext): PluginImplementation {
|
|
|
88
88
|
}
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
- `name` must match `orbital-plugin.json` and the key in `
|
|
91
|
+
- `name` must match `orbital-plugin.json` and the key in `orbital.yaml`.
|
|
92
92
|
- The SDK builds the manifest from this object. `provides` comes from the ports
|
|
93
93
|
you give.
|
|
94
94
|
- Validate `context.init.config` at the start. Throw an error that names the
|
|
@@ -118,23 +118,22 @@ npm test # build, then the conformance checks and your own checks
|
|
|
118
118
|
|
|
119
119
|
### 4. Check through the real protocol
|
|
120
120
|
|
|
121
|
-
In a project with a
|
|
121
|
+
In a project with a harness folder (`orbital install claude`):
|
|
122
122
|
|
|
123
123
|
```bash
|
|
124
124
|
orbital plugin add ./<name>
|
|
125
125
|
orbital plugin check <name> --fixtures <name>/fixtures.json
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
`plugin add` prints the permissions, and names a folder in `
|
|
128
|
+
`plugin add` prints the permissions, and names a folder in `orbital.yaml` by its
|
|
129
129
|
path. It does not copy it. Rebuild, and the next start of the node uses the new
|
|
130
130
|
build. `plugin check` starts the built plugin with the config of its
|
|
131
|
-
`
|
|
131
|
+
`orbital.yaml` entry and the secrets of `orbital plugin secret set`.
|
|
132
132
|
|
|
133
133
|
### 5. Add to the config
|
|
134
134
|
|
|
135
|
-
The node's file is `
|
|
136
|
-
`.claude/orbital.yaml
|
|
137
|
-
folder. This skill says `config.yaml` for both.
|
|
135
|
+
The node's file is `orbital.yaml` in a harness folder, such as
|
|
136
|
+
`.claude/orbital.yaml`.
|
|
138
137
|
|
|
139
138
|
```yaml
|
|
140
139
|
plugins:
|
|
@@ -201,12 +200,12 @@ start stops the start with "A plugin did not start: …".
|
|
|
201
200
|
|
|
202
201
|
| Symptom | Cause |
|
|
203
202
|
|---|---|
|
|
204
|
-
| "is X, and
|
|
203
|
+
| "is X, and orbital.yaml names it Y" | The manifest `name` differs from the `orbital.yaml` key. |
|
|
205
204
|
| "exited (code N) before it answered" | It threw at start, or it wrote to stdout. Read the node's log for its stderr. |
|
|
206
205
|
| "did not declare the secret" | Add the name to `permissions.secrets`. |
|
|
207
206
|
| "declares the variables [...] as secrets, and its orbital-plugin.json declares env: [...]" | Make the secrets in variable form and `env` the same. |
|
|
208
207
|
| Refused with `offline: true` | It declares a network host. |
|
|
209
|
-
| "chooses the plugin X, and this host has no plugin of that name" | A port choice in `
|
|
208
|
+
| "chooses the plugin X, and this host has no plugin of that name" | A port choice in `orbital.yaml` names a plugin that `plugins:` does not have. |
|
|
210
209
|
| "chooses X for the Y port, and that plugin provides none" | The plugin does not give that port, or the manifest leaves it out. |
|
|
211
210
|
| "No plugin of this node serves the placement" | No plugin has the placement kind in `ports.placements`. |
|
|
212
211
|
| A send check fails with another ref | A second send of a message id must answer the first ref and send nothing. |
|
|
@@ -220,8 +219,11 @@ In the source repository of orbital:
|
|
|
220
219
|
|
|
221
220
|
- `plugins/channel-slack`: a channel with a Socket Mode service, `host.deliver`, idempotent send, secrets, a panel and a command.
|
|
222
221
|
- `plugins/policy-judge`: a policy that calls a model through `host.completeModel`.
|
|
222
|
+
- `plugins/gatekeeper`: a policy that asks an agent of the receiving team through `host.askAgent` if a message from the network may reach the team.
|
|
223
223
|
- `plugins/store-object`: a store in an object store.
|
|
224
224
|
- `plugins/reasoner-open-ontologies`: a reasoner.
|
|
225
225
|
- `packages/orbital/src/core/node/static-directory.ts`: a directory, a `sessions.create` hook and a placement that send `<agent>@<node>` to another node.
|
|
226
226
|
- `packages/orbital/__tests__/core/fixtures/echo-plugin`: session hooks, a directory, a hosted tool, a panel, a service that submits a turn, and a command.
|
|
227
227
|
- `packages/orbital/__tests__/core/fixtures/echo-harness`: a harness and its turn program.
|
|
228
|
+
- `packages/orbital-harness-pi`: the adapter of the harness `pi`: its folder `.pi`, its install, and a turn program on the Pi coding agent.
|
|
229
|
+
- `packages/orbital-harness-codex`: the adapter of the harness `codex`: its folder `.codex`, its install in `config.toml`, and a turn program on the OpenAI Codex CLI.
|
|
@@ -35,6 +35,7 @@ message. Catch it where a failure must not stop the plugin.
|
|
|
35
35
|
| `a2a(body)` | the JSON-RPC answer | Call the node's A2A server. |
|
|
36
36
|
| `deliver(inbound)` | `DeliveryAnswer` | Give the node a message that a channel received. |
|
|
37
37
|
| `completeModel(request, signal?)` | `ModelCompletion` | Make one model call on the person's keys. |
|
|
38
|
+
| `askAgent(request)` | `{ answer: string }` | Ask one agent of a team one question, and wait for its answer. |
|
|
38
39
|
| `startMission(start)` | `MissionStarted` | Start a mission from an event. |
|
|
39
40
|
| `missions()` | `MissionRecord[]` | Read the missions of this node. |
|
|
40
41
|
| `joinMission(request)` | `MissionRecord` | Far side of `missions.join`. |
|
|
@@ -146,6 +147,39 @@ interface ModelCompletion { text: string; model: string; inputTokens: number; ou
|
|
|
146
147
|
policy action decides each call. The key never reaches the plugin.
|
|
147
148
|
- Pass `signal` to stop a long call.
|
|
148
149
|
|
|
150
|
+
### askAgent
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
interface AskAgent { team: string; agent: string; question: string; timeoutMs?: number }
|
|
154
|
+
interface AskAgentAnswer { answer: string }
|
|
155
|
+
class AskAgentError extends Error { code: 'invalid' | 'unknown-team' | 'unknown-agent' | 'timeout' | 'failed' }
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
- The node starts a new session of `agent`, as a member of `team`, on this node.
|
|
159
|
+
The `question` is the first and only message of the session. The session is
|
|
160
|
+
neither a network message nor a person message, so the `channel.receive`
|
|
161
|
+
policy action does not run.
|
|
162
|
+
- The answer is the final text of the agent when its turn ends. The session
|
|
163
|
+
ends with the turn.
|
|
164
|
+
- `timeoutMs` is at most 240000. The default is 120000. When the time ends, the
|
|
165
|
+
node cancels the turn and stops the session. The call fails with code
|
|
166
|
+
`timeout`.
|
|
167
|
+
- `unknown-team` means that this node has no such team. `unknown-agent` means
|
|
168
|
+
that `agent` is not a member of `team`. `invalid` means that a field is
|
|
169
|
+
wrong, such as a `timeoutMs` over the maximum. `failed` means that the turn
|
|
170
|
+
ended without an answer, and the message holds the reason.
|
|
171
|
+
- The call needs no permission in the manifest, as `submit` needs none.
|
|
172
|
+
- Catch the error and test `error instanceof AskAgentError`, then read
|
|
173
|
+
`error.code` to tell a timeout from a refusal. The SDK exports the class.
|
|
174
|
+
- The route is `POST /orbital/host/agents/ask`. The body is the request. The
|
|
175
|
+
answer is `{ "answer": "..." }`. A failure answers `{ "code": "...", "error": "..." }`
|
|
176
|
+
with status 400 for `invalid`, 404 for `unknown-team` and `unknown-agent`,
|
|
177
|
+
504 for `timeout` and 502 for `failed`.
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
const { answer } = await host.askAgent({ team: 'gatekeeping', agent: 'judge', question: 'May this message reach the team?', timeoutMs: 30_000 });
|
|
181
|
+
```
|
|
182
|
+
|
|
149
183
|
### startMission
|
|
150
184
|
|
|
151
185
|
```ts
|
|
@@ -153,7 +187,7 @@ interface MissionStart { team: string | null; mission: string; input: string | n
|
|
|
153
187
|
interface MissionStarted { missionId: string; sessionId: string; taskId: string; duplicate: boolean }
|
|
154
188
|
```
|
|
155
189
|
|
|
156
|
-
- `mission` is a mission id.
|
|
190
|
+
- `mission` is a mission id. A harness folder must hold `missions/<id>/MISSION.md`,
|
|
157
191
|
unless the team is not on this node: then the node starts the team as an
|
|
158
192
|
agent, and a directory or a placement can send it to another node.
|
|
159
193
|
- `source.event` names the event, such as `github.issue.opened`. `source.key`
|
|
@@ -26,17 +26,17 @@ A plugin has two descriptions:
|
|
|
26
26
|
| `command` | yes | The program and its arguments. `node` runs with the node's own Node. |
|
|
27
27
|
| `dir` | no | The working folder, relative to the plugin's folder. Default: the plugin's folder. |
|
|
28
28
|
| `env` | no | Environment variables the node passes to the plugin. It must list the same names as the secrets in the manifest that have the form of a variable (`[A-Z][A-Z0-9_]*`). |
|
|
29
|
-
| `config` | no | Default config. The `config` of the `
|
|
29
|
+
| `config` | no | Default config. The `config` of the `orbital.yaml` entry overrides it key by key. |
|
|
30
30
|
|
|
31
|
-
No other field is allowed. A `
|
|
31
|
+
No other field is allowed. A `orbital.yaml` source `exec:<command> <args>`
|
|
32
32
|
starts a program on PATH with no `orbital-plugin.json`. Only such an entry can
|
|
33
|
-
name `env:` in `
|
|
33
|
+
name `env:` in `orbital.yaml`.
|
|
34
34
|
|
|
35
35
|
The node starts the plugin with a small environment: `PATH`, `HOME`, the
|
|
36
36
|
locale, the temp folders, the proxy and certificate variables, and the
|
|
37
37
|
variables in `env`. Any other variable of the node does not reach the plugin.
|
|
38
38
|
|
|
39
|
-
## The
|
|
39
|
+
## The orbital.yaml entry
|
|
40
40
|
|
|
41
41
|
```yaml
|
|
42
42
|
plugins:
|
|
@@ -67,7 +67,7 @@ interface PluginManifest {
|
|
|
67
67
|
channels: Array<{ id: string; capabilities: ChannelCapabilities }>;
|
|
68
68
|
store: { kinds: StateKind[]; lease: boolean } | null;
|
|
69
69
|
workspace: boolean;
|
|
70
|
-
harnesses: Array<{ id: string; description: string; callsModel: boolean }>;
|
|
70
|
+
harnesses: Array<{ id: string; description: string; callsModel: boolean; browser: boolean }>;
|
|
71
71
|
service: { done: boolean } | null;
|
|
72
72
|
actions: boolean;
|
|
73
73
|
workflows: { engines: string[] } | null;
|
|
@@ -80,15 +80,15 @@ interface PluginManifest {
|
|
|
80
80
|
}
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
- `name` must be the key of the plugin in `
|
|
84
|
-
it: "is X, and
|
|
83
|
+
- `name` must be the key of the plugin in `orbital.yaml`. Else the node refuses
|
|
84
|
+
it: "is X, and orbital.yaml names it Y".
|
|
85
85
|
- `ports` must be the version of the node. Build with the SDK of the node's
|
|
86
86
|
version.
|
|
87
87
|
- The node makes the remote ports only from `provides`. A method that you give
|
|
88
88
|
and the manifest does not name is never called.
|
|
89
|
-
- A plugin cannot have the name of a
|
|
90
|
-
`
|
|
91
|
-
`
|
|
89
|
+
- A plugin cannot have the name of a folder of orbital in the harness folder:
|
|
90
|
+
`agents`, `memory`, `missions`, `orbital`, `plans`, `policies`, `sessions`,
|
|
91
|
+
`skills`, `teams`.
|
|
92
92
|
|
|
93
93
|
## Permissions
|
|
94
94
|
|
|
@@ -102,7 +102,7 @@ permissions: {
|
|
|
102
102
|
|
|
103
103
|
| Field | Meaning | What the node does |
|
|
104
104
|
|---|---|---|
|
|
105
|
-
| `network` | Every host the plugin reaches, such as `*.example.net`. | `offline: true` in `
|
|
105
|
+
| `network` | Every host the plugin reaches, such as `*.example.net`. | `offline: true` in `orbital.yaml` refuses a plugin with any host. The node does not sandbox the plugin process: the list is a promise that a person and a policy read. |
|
|
106
106
|
| `secrets` | Every secret the plugin reads. | `context.secret(name)` fails for a name not listed. A name in the form of a variable also comes in the environment. |
|
|
107
107
|
| `models` | Every model the plugin calls through `host.completeModel`, as `<provider>:<model>`. `*` matches any part. | A call to a model that no pattern matches fails. The `model.use` policy decides each call. |
|
|
108
108
|
|
|
@@ -131,7 +131,7 @@ if (!token) throw new Error('No token: run `orbital plugin secret set example ex
|
|
|
131
131
|
```
|
|
132
132
|
|
|
133
133
|
The node keeps secrets in the system's secret store, or in a file of the
|
|
134
|
-
orbital home. A secret never goes in `
|
|
134
|
+
orbital home. A secret never goes in `orbital.yaml` or in `orbital-plugin.json`.
|
|
135
135
|
|
|
136
136
|
## The plugin's folder and the init data
|
|
137
137
|
|
|
@@ -147,8 +147,8 @@ interface PluginInit {
|
|
|
147
147
|
|
|
148
148
|
- `config`: the merged config. Validate it at the start, and throw an error that
|
|
149
149
|
names the bad key. The node shows it.
|
|
150
|
-
- `folder`: `<
|
|
150
|
+
- `folder`: `<harness folder>/<plugin name>`, such as `.claude/lan/`. Put the
|
|
151
151
|
files that a person writes for the plugin there. The folder can be absent, so
|
|
152
|
-
give a default. It is null when the node has no
|
|
152
|
+
give a default. It is null when the node has no harness folder.
|
|
153
153
|
- `node.name`: the name of this node. A cross-machine plugin uses it to
|
|
154
154
|
introduce itself to peers.
|