@holokai/neuron-sdk 0.5.0 → 0.6.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.
- package/README.md +74 -45
- package/dist/index.d.mts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +35 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +35 -5
- package/dist/index.mjs.map +1 -1
- package/dist/ipc.d.mts +1 -1
- package/dist/ipc.d.ts +1 -1
- package/dist/{transport-Ckcwj-Do.d.mts → transport-Va7tTRVO.d.mts} +125 -4
- package/dist/{transport-Ckcwj-Do.d.ts → transport-Va7tTRVO.d.ts} +125 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ pnpm add @holokai/neuron-sdk
|
|
|
21
21
|
|
|
22
22
|
Runs in **Node 20+** (uses the global `fetch` and `ReadableStream`) and **modern browsers** (Chromium, Firefox, Safari latest). The package has no `node:*` imports — Holokai Desktop / Electron and the in-browser neuron-tester both consume the same SDK build.
|
|
23
23
|
|
|
24
|
-
> **Gateway compatibility (protocol 1.
|
|
24
|
+
> **Gateway compatibility (protocol 1.2.0; HOL-2926, HOL-4326).** Since protocol
|
|
25
25
|
> 1.1.0 the SDK advertises each capability's contract (`description` +
|
|
26
26
|
> input/output JSON Schemas) on the register frame. The gateway validates
|
|
27
27
|
> register frames **strictly**, so a gateway older than HOL-2926 rejects
|
|
@@ -114,6 +114,29 @@ Examples:
|
|
|
114
114
|
- `desktop/mcp/drive.read` — user's Google Drive, scoped to that user
|
|
115
115
|
- `org/01HBQ8ZK/custom-tool` — org-specific capability
|
|
116
116
|
|
|
117
|
+
### Composing capabilities (`composes`)
|
|
118
|
+
|
|
119
|
+
A capability namespaced `skill/<name>/<id>` may declare `composes`: the other
|
|
120
|
+
capability types it draws on to carry out one recurring job. Set it on
|
|
121
|
+
`defineCapability(...)`, on `neuron.handle(def, opts, fn)`'s `opts`, or on the
|
|
122
|
+
legacy registration object — it is only valid on a `skill/*` type, must name
|
|
123
|
+
1 to 64 distinct capability types, and none of them may itself be `skill/*`.
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
const weeklyReport = defineCapability({
|
|
127
|
+
type: 'skill/weekly-report/01',
|
|
128
|
+
description: 'Pulls last week\'s issues and writes a summary.',
|
|
129
|
+
input: z.object({}),
|
|
130
|
+
output: z.object({ summary: z.string() }),
|
|
131
|
+
composes: ['mcp/linear/list_issues', 'holo/37bfe871'],
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`composes` is carried on the register frame's capability object (gateway
|
|
136
|
+
protocol 1.2.0, HOL-4326) — **the gateway must be running 1.2.0 or later
|
|
137
|
+
before deploying an SDK version that sends it**, or the gateway rejects the
|
|
138
|
+
register frame outright.
|
|
139
|
+
|
|
117
140
|
### Namespace authorization
|
|
118
141
|
|
|
119
142
|
The gateway enforces which identities may advertise which namespaces:
|
|
@@ -843,69 +866,75 @@ The `auth` callback isn't returning a valid token. If using a refresh flow, ensu
|
|
|
843
866
|
|
|
844
867
|
## Releasing (maintainers)
|
|
845
868
|
|
|
846
|
-
`@holokai/neuron-sdk` is published to public npm
|
|
847
|
-
|
|
848
|
-
|
|
869
|
+
`@holokai/neuron-sdk` is published to public npm by BigBrain's Release
|
|
870
|
+
workflow (`.github/workflows/release.yml`). There is no npm token on anyone's
|
|
871
|
+
laptop and none stored in the repo, and pushing a tag publishes nothing.
|
|
872
|
+
|
|
873
|
+
### Pick the version
|
|
874
|
+
|
|
875
|
+
Follow semver:
|
|
876
|
+
|
|
877
|
+
- Pilot phase, we are in **`0.x`**. Because the protocol is still absorbing
|
|
878
|
+
pilot-developer feedback, a **breaking** change bumps the **minor**
|
|
879
|
+
(`0.2.0` to `0.3.0`); a backward-compatible change or fix bumps the
|
|
880
|
+
**patch** (`0.2.0` to `0.2.1`). This is the standard `0.x` convention and
|
|
881
|
+
is what buys us room to break things on feedback without a major bump.
|
|
882
|
+
- Hold **`1.0.0`** until the public API has survived pilot feedback and we're
|
|
883
|
+
ready to commit to "breaking changes bump the major." Jumping to `1.0.0`
|
|
884
|
+
is a stability promise, not a milestone. Don't spend it early.
|
|
849
885
|
|
|
850
886
|
### Cut a release
|
|
851
887
|
|
|
852
|
-
1. **
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
The tag is prefixed `neuron-sdk-v` (the Python SDK uses
|
|
869
|
-
`neuron-sdk-python-v`) so the two never collide.
|
|
870
|
-
4. **`publish-neuron-sdk.yml`** fires on the tag: it asserts the tag version
|
|
871
|
-
matches `package.json`, runs the `prepublish` gate (`build` + `test`), and
|
|
872
|
-
publishes to npm. The `npm` GitHub Environment gates the publish behind a
|
|
873
|
-
**required-reviewer approval** — approve the run under Actions, and on success
|
|
874
|
-
the new version is live at
|
|
888
|
+
1. Open **Actions, Release, Run workflow** on branch `main`.
|
|
889
|
+
2. Fill **neuron-sdk** with the new version (`X.Y.Z`). Leave the other lines
|
|
890
|
+
(base, embedded, synapse) empty unless they are being released too; an
|
|
891
|
+
empty line is not touched. Tick **plan only** to read the plan without
|
|
892
|
+
cutting anything.
|
|
893
|
+
3. The plan job checks the version (shaped `X.Y.Z`, not older than
|
|
894
|
+
`packages/neuron-sdk/package.json`, tag `neuron-sdk-vX.Y.Z` not taken) and
|
|
895
|
+
writes the plan to the run summary.
|
|
896
|
+
4. A reviewer of the `release` environment approves the run. That is the only
|
|
897
|
+
approval.
|
|
898
|
+
5. The cut writes the version into `packages/neuron-sdk/package.json`,
|
|
899
|
+
commits it to `main`, tags `neuron-sdk-vX.Y.Z` and pushes both. If the file
|
|
900
|
+
already carries the version (a bump merged by pull request), it only tags.
|
|
901
|
+
6. From the commit the cut pushed, the run builds and tests the SDK (the
|
|
902
|
+
`prepublish` turbo task), packs it, and publishes the tarball to npm from a
|
|
903
|
+
GitHub-hosted runner. On success the new version is live at
|
|
875
904
|
[`@holokai/neuron-sdk`](https://www.npmjs.com/package/@holokai/neuron-sdk).
|
|
876
905
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
> unaffected. Add the flag back if bigbrain ever goes public.
|
|
906
|
+
The tag is prefixed `neuron-sdk-v` (the Python SDK uses `neuron-sdk-python-v`)
|
|
907
|
+
so the two never collide. The workflow file's header comments describe every
|
|
908
|
+
job and how to recover a failed run.
|
|
881
909
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
910
|
+
> No `--provenance`: bigbrain is a private repo, so npm's provenance
|
|
911
|
+
> attestation (a public transparency-log link back to the source) adds little
|
|
912
|
+
> and has historically errored from private repos. Trusted-publishing auth is
|
|
913
|
+
> unaffected. Add the flag back if bigbrain ever goes public.
|
|
885
914
|
|
|
886
915
|
### Publish auth: npm Trusted Publishing (one-time setup)
|
|
887
916
|
|
|
888
|
-
CI authenticates to npm via **OIDC trusted publishing
|
|
917
|
+
CI authenticates to npm via **OIDC trusted publishing**: GitHub Actions
|
|
889
918
|
exchanges a short-lived OIDC token for publish rights, so no long-lived
|
|
890
|
-
`NPM_TOKEN` exists to leak.
|
|
891
|
-
publishing. It requires two one-time setups:
|
|
919
|
+
`NPM_TOKEN` exists to leak. It requires two one-time setups:
|
|
892
920
|
|
|
893
921
|
1. **On npmjs.com** (a package admin, once): open the
|
|
894
922
|
[`@holokai/neuron-sdk`](https://www.npmjs.com/package/@holokai/neuron-sdk)
|
|
895
|
-
package
|
|
923
|
+
package, **Settings, Trusted Publisher, GitHub Actions**, and register:
|
|
896
924
|
- Organization / repository: `holok-ai/bigbrain`
|
|
897
|
-
- Workflow filename: `
|
|
925
|
+
- Workflow filename: `release.yml`
|
|
898
926
|
- Environment: `npm`
|
|
899
|
-
2. **In this repo** (once):
|
|
900
|
-
|
|
901
|
-
|
|
927
|
+
2. **In this repo** (once): the **`npm`** GitHub Environment (Settings,
|
|
928
|
+
Environments) with no required reviewer, because the Release run's one
|
|
929
|
+
approval is on its cut, and a deployment branch rule of `main` only, so a
|
|
930
|
+
copy of `release.yml` on another branch cannot publish to npm.
|
|
902
931
|
|
|
903
932
|
Until the trusted publisher is registered, the `npm publish` step fails with an
|
|
904
|
-
auth error
|
|
933
|
+
auth error. That is the expected "not set up yet" signal, not a code bug.
|
|
905
934
|
|
|
906
935
|
### Manual publish (fallback / first bootstrap)
|
|
907
936
|
|
|
908
|
-
The
|
|
937
|
+
The Release workflow is the norm. If you ever need to publish by hand (e.g. to bootstrap
|
|
909
938
|
the very first version before trusted publishing is configured):
|
|
910
939
|
|
|
911
940
|
```sh
|
|
@@ -915,6 +944,6 @@ pnpm run publish:npm # runs the prepublishOnly build+test gate, then publi
|
|
|
915
944
|
```
|
|
916
945
|
|
|
917
946
|
This uses your own `npm login` credentials and skips the CI approval gate —
|
|
918
|
-
prefer the
|
|
947
|
+
prefer the Release workflow for anything a developer will consume.
|
|
919
948
|
|
|
920
949
|
<!-- ci: force full build on the self-hosted runner (HOL-2028 verify); safe to remove -->
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { J as JsonSchema, C as Capability, a as CapabilityHandleOptions, S as ServerToNeuronFrame, N as NeuronLogger, T as Transport, A as AuthCallback, H as Handler, b as CapabilityRegistration, c as TransportCallbacks, d as ClientFrame, P as PostPath } from './transport-
|
|
2
|
-
export { e as HandlerContext, f as HeartbeatInFlightEntry, L as LeasePayload, g as TaskPayload } from './transport-
|
|
1
|
+
import { J as JsonSchema, C as Capability, a as CapabilityHandleOptions, S as ServerToNeuronFrame, N as NeuronLogger, T as Transport, A as AuthCallback, H as Handler, b as CapabilityRegistration, c as TransportCallbacks, d as ClientFrame, P as PostPath } from './transport-Va7tTRVO.mjs';
|
|
2
|
+
export { e as HandlerContext, f as HeartbeatInFlightEntry, L as LeasePayload, g as TaskPayload } from './transport-Va7tTRVO.mjs';
|
|
3
3
|
import { EventEmitter } from 'eventemitter3';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { ErrorObject } from 'ajv';
|
|
@@ -48,6 +48,12 @@ interface CapabilityDefInit<TIn extends z.ZodTypeAny, TOut extends z.ZodTypeAny>
|
|
|
48
48
|
* Bounded `[1_000, 3_600_000]` (1s..1h) by `CapabilitySchema`.
|
|
49
49
|
*/
|
|
50
50
|
leaseTtlMs?: number;
|
|
51
|
+
/**
|
|
52
|
+
* Capability types this `skill/*` capability composes (HOL-4326). Only
|
|
53
|
+
* valid when `type` is namespaced `skill/*` — enforced by
|
|
54
|
+
* `CapabilitySchema` on the gateway.
|
|
55
|
+
*/
|
|
56
|
+
composes?: readonly string[];
|
|
51
57
|
}
|
|
52
58
|
/**
|
|
53
59
|
* A typed capability definition. The phantom `_input` / `_output` fields
|
|
@@ -68,6 +74,8 @@ interface CapabilityDef<TIn = unknown, TOut = unknown> {
|
|
|
68
74
|
readonly concurrency?: number;
|
|
69
75
|
/** See {@link CapabilityDefInit.leaseTtlMs}. */
|
|
70
76
|
readonly leaseTtlMs?: number;
|
|
77
|
+
/** See {@link CapabilityDefInit.composes}. */
|
|
78
|
+
readonly composes?: readonly string[];
|
|
71
79
|
/** Original zod schemas — kept for runtime validation in handlers if needed. */
|
|
72
80
|
readonly _zod: {
|
|
73
81
|
input: z.ZodType<TIn>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { J as JsonSchema, C as Capability, a as CapabilityHandleOptions, S as ServerToNeuronFrame, N as NeuronLogger, T as Transport, A as AuthCallback, H as Handler, b as CapabilityRegistration, c as TransportCallbacks, d as ClientFrame, P as PostPath } from './transport-
|
|
2
|
-
export { e as HandlerContext, f as HeartbeatInFlightEntry, L as LeasePayload, g as TaskPayload } from './transport-
|
|
1
|
+
import { J as JsonSchema, C as Capability, a as CapabilityHandleOptions, S as ServerToNeuronFrame, N as NeuronLogger, T as Transport, A as AuthCallback, H as Handler, b as CapabilityRegistration, c as TransportCallbacks, d as ClientFrame, P as PostPath } from './transport-Va7tTRVO.js';
|
|
2
|
+
export { e as HandlerContext, f as HeartbeatInFlightEntry, L as LeasePayload, g as TaskPayload } from './transport-Va7tTRVO.js';
|
|
3
3
|
import { EventEmitter } from 'eventemitter3';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { ErrorObject } from 'ajv';
|
|
@@ -48,6 +48,12 @@ interface CapabilityDefInit<TIn extends z.ZodTypeAny, TOut extends z.ZodTypeAny>
|
|
|
48
48
|
* Bounded `[1_000, 3_600_000]` (1s..1h) by `CapabilitySchema`.
|
|
49
49
|
*/
|
|
50
50
|
leaseTtlMs?: number;
|
|
51
|
+
/**
|
|
52
|
+
* Capability types this `skill/*` capability composes (HOL-4326). Only
|
|
53
|
+
* valid when `type` is namespaced `skill/*` — enforced by
|
|
54
|
+
* `CapabilitySchema` on the gateway.
|
|
55
|
+
*/
|
|
56
|
+
composes?: readonly string[];
|
|
51
57
|
}
|
|
52
58
|
/**
|
|
53
59
|
* A typed capability definition. The phantom `_input` / `_output` fields
|
|
@@ -68,6 +74,8 @@ interface CapabilityDef<TIn = unknown, TOut = unknown> {
|
|
|
68
74
|
readonly concurrency?: number;
|
|
69
75
|
/** See {@link CapabilityDefInit.leaseTtlMs}. */
|
|
70
76
|
readonly leaseTtlMs?: number;
|
|
77
|
+
/** See {@link CapabilityDefInit.composes}. */
|
|
78
|
+
readonly composes?: readonly string[];
|
|
71
79
|
/** Original zod schemas — kept for runtime validation in handlers if needed. */
|
|
72
80
|
readonly _zod: {
|
|
73
81
|
input: z.ZodType<TIn>;
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,11 @@ var CapabilityTypeSchema = zod.z.string().min(1).regex(
|
|
|
17
17
|
"capability type must be namespaced: {owner}/{package}[/{name}][.{variant}]"
|
|
18
18
|
);
|
|
19
19
|
var UserIdSchema = zod.z.string().min(1);
|
|
20
|
+
var SKILL_CAPABILITY_PREFIX = "skill/";
|
|
21
|
+
function isSkillCapabilityType(type) {
|
|
22
|
+
return type.startsWith(SKILL_CAPABILITY_PREFIX);
|
|
23
|
+
}
|
|
24
|
+
var MAX_COMPOSED_CAPABILITIES = 64;
|
|
20
25
|
var JsonSchemaSchema = zod.z.unknown();
|
|
21
26
|
var CapabilityScopeSchema = zod.z.union([
|
|
22
27
|
zod.z.literal("any"),
|
|
@@ -69,8 +74,29 @@ var CapabilitySchema = zod.z.object({
|
|
|
69
74
|
* heartbeat budget should be the limiter, not a single
|
|
70
75
|
* capability).
|
|
71
76
|
*/
|
|
72
|
-
leaseTtlMs: zod.z.number().int().min(1e3).max(60 * 60 * 1e3).optional()
|
|
73
|
-
|
|
77
|
+
leaseTtlMs: zod.z.number().int().min(1e3).max(60 * 60 * 1e3).optional(),
|
|
78
|
+
/**
|
|
79
|
+
* Capability types this `skill/*` capability composes (HOL-4326).
|
|
80
|
+
* Only valid on a `skill/*` type — enforced below, since a non-skill
|
|
81
|
+
* type carrying `composes` is a modeling error the gateway should
|
|
82
|
+
* reject rather than silently ignore.
|
|
83
|
+
*/
|
|
84
|
+
composes: zod.z.array(CapabilityTypeSchema).min(1).max(MAX_COMPOSED_CAPABILITIES).refine(
|
|
85
|
+
(types) => new Set(types).size === types.length,
|
|
86
|
+
"composes must not contain duplicate capability types"
|
|
87
|
+
).refine(
|
|
88
|
+
(types) => types.every((t) => !isSkillCapabilityType(t)),
|
|
89
|
+
"composes must not contain skill/* capability types"
|
|
90
|
+
).optional()
|
|
91
|
+
}).strict().superRefine((cap, ctx) => {
|
|
92
|
+
if (cap.composes !== void 0 && !isSkillCapabilityType(cap.type)) {
|
|
93
|
+
ctx.addIssue({
|
|
94
|
+
code: zod.z.ZodIssueCode.custom,
|
|
95
|
+
message: "composes is only valid on a skill/* capability type",
|
|
96
|
+
path: ["composes"]
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
});
|
|
74
100
|
var LeaseIdSchema = zod.z.string().min(1);
|
|
75
101
|
var TaskPayloadSchema = zod.z.object({
|
|
76
102
|
taskId: zod.z.string().min(1),
|
|
@@ -209,7 +235,7 @@ zod.z.discriminatedUnion("type", [
|
|
|
209
235
|
]);
|
|
210
236
|
|
|
211
237
|
// ../neuron-protocol/src/transport.ts
|
|
212
|
-
var PROTOCOL_VERSION = "1.
|
|
238
|
+
var PROTOCOL_VERSION = "1.2.0";
|
|
213
239
|
var SSE_PATH = "/neuron/events";
|
|
214
240
|
var SSE_EVENT = {
|
|
215
241
|
registered: "registered",
|
|
@@ -235,6 +261,7 @@ function defineCapability(init) {
|
|
|
235
261
|
scope: init.scope,
|
|
236
262
|
concurrency: init.concurrency,
|
|
237
263
|
leaseTtlMs: init.leaseTtlMs,
|
|
264
|
+
composes: init.composes,
|
|
238
265
|
_zod: { input: init.input, output: init.output }
|
|
239
266
|
};
|
|
240
267
|
}
|
|
@@ -260,6 +287,7 @@ function toRegistrationCapability(def, opts) {
|
|
|
260
287
|
);
|
|
261
288
|
}
|
|
262
289
|
const leaseTtlMs = opts?.leaseTtlMs ?? def.leaseTtlMs;
|
|
290
|
+
const composes = opts?.composes ?? def.composes;
|
|
263
291
|
return {
|
|
264
292
|
type: def.type,
|
|
265
293
|
scope,
|
|
@@ -270,7 +298,8 @@ function toRegistrationCapability(def, opts) {
|
|
|
270
298
|
description: opts?.description ?? def.description,
|
|
271
299
|
inputSchema: def.inputSchema,
|
|
272
300
|
outputSchema: def.outputSchema,
|
|
273
|
-
...typeof leaseTtlMs === "number" ? { leaseTtlMs } : {}
|
|
301
|
+
...typeof leaseTtlMs === "number" ? { leaseTtlMs } : {},
|
|
302
|
+
...composes !== void 0 ? { composes: [...composes] } : {}
|
|
274
303
|
};
|
|
275
304
|
}
|
|
276
305
|
|
|
@@ -979,7 +1008,8 @@ var Neuron = class extends eventemitter3.EventEmitter {
|
|
|
979
1008
|
...reg.description !== void 0 ? { description: reg.description } : {},
|
|
980
1009
|
...reg.inputSchema !== void 0 ? { inputSchema: reg.inputSchema } : {},
|
|
981
1010
|
...reg.outputSchema !== void 0 ? { outputSchema: reg.outputSchema } : {},
|
|
982
|
-
...typeof reg.leaseTtlMs === "number" ? { leaseTtlMs: reg.leaseTtlMs } : {}
|
|
1011
|
+
...typeof reg.leaseTtlMs === "number" ? { leaseTtlMs: reg.leaseTtlMs } : {},
|
|
1012
|
+
...reg.composes !== void 0 ? { composes: reg.composes } : {}
|
|
983
1013
|
};
|
|
984
1014
|
inputSchema = reg.inputSchema;
|
|
985
1015
|
outputSchema = reg.outputSchema;
|