@neuralis/brain-core 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -3
- package/app/filesystem/api/README.md +1 -1
- package/app/filesystem/api/index.ts +24 -24
- package/app/filesystem/store/README.md +1 -1
- package/app/filesystem/store/filesystemStore.ts +11 -3
- package/app/filesystem/store/listSlice.ts +1 -0
- package/app/filesystem/ui/FileEditor.tsx +42 -4
- package/app/filesystem/ui/GitControlPanel.tsx +0 -1
- package/app/filesystem/ui/GitDiffView.tsx +12 -5
- package/app/filesystem/ui/GitFileDiff.tsx +15 -9
- package/app/filesystem/ui/PendingChangesPanel.tsx +95 -34
- package/app/filesystem/ui/README.md +9 -9
- package/app/filesystem/ui/SourcesPanel.tsx +1 -3
- package/app/filesystem/ui/Toolbar.tsx +5 -4
- package/app/filesystem/ui/VectorHealthPanel.tsx +43 -14
- package/dist/app/host.js +40 -40
- package/dist/src/config/env.d.ts +4 -0
- package/dist/src/config/env.d.ts.map +1 -1
- package/dist/src/config/env.js +13 -0
- package/dist/src/config/env.js.map +1 -1
- package/dist/src/connectors/LocalDiskConnector.d.ts +9 -1
- package/dist/src/connectors/LocalDiskConnector.d.ts.map +1 -1
- package/dist/src/connectors/LocalDiskConnector.js +51 -2
- package/dist/src/connectors/LocalDiskConnector.js.map +1 -1
- package/dist/src/domain/healthTypes.d.ts +1 -7
- package/dist/src/domain/healthTypes.d.ts.map +1 -1
- package/dist/src/domain/pendingChange.d.ts +31 -0
- package/dist/src/domain/pendingChange.d.ts.map +1 -1
- package/dist/src/domain/pendingChange.js.map +1 -1
- package/dist/src/routes/health.js +2 -3
- package/dist/src/routes/health.js.map +1 -1
- package/dist/src/routes/pending.d.ts +7 -7
- package/dist/src/routes/pending.d.ts.map +1 -1
- package/dist/src/routes/pending.js +21 -21
- package/dist/src/routes/pending.js.map +1 -1
- package/dist/src/services/FilesystemService.d.ts +13 -61
- package/dist/src/services/FilesystemService.d.ts.map +1 -1
- package/dist/src/services/FilesystemService.js +47 -189
- package/dist/src/services/FilesystemService.js.map +1 -1
- package/dist/src/services/FilesystemUiService.d.ts +33 -1
- package/dist/src/services/FilesystemUiService.d.ts.map +1 -1
- package/dist/src/services/FilesystemUiService.js +50 -1
- package/dist/src/services/FilesystemUiService.js.map +1 -1
- package/dist/src/store/PendingChangeStore.d.ts +3 -0
- package/dist/src/store/PendingChangeStore.d.ts.map +1 -1
- package/dist/src/store/PendingChangeStore.js +84 -25
- package/dist/src/store/PendingChangeStore.js.map +1 -1
- package/dist/src/usecases/orphanedPending.d.ts +2 -2
- package/dist/src/usecases/orphanedPending.d.ts.map +1 -1
- package/dist/src/usecases/orphanedPending.js +5 -3
- package/dist/src/usecases/orphanedPending.js.map +1 -1
- package/dist/src/usecases/pendingListing.d.ts +34 -0
- package/dist/src/usecases/pendingListing.d.ts.map +1 -0
- package/dist/src/usecases/pendingListing.js +244 -0
- package/dist/src/usecases/pendingListing.js.map +1 -0
- package/dist/src/usecases/vectorMaintenance.d.ts +6 -9
- package/dist/src/usecases/vectorMaintenance.d.ts.map +1 -1
- package/dist/src/usecases/vectorMaintenance.js +4 -13
- package/dist/src/usecases/vectorMaintenance.js.map +1 -1
- package/dist/src/vector/InMemoryVectorIndex.d.ts.map +1 -1
- package/dist/src/vector/InMemoryVectorIndex.js +30 -9
- package/dist/src/vector/InMemoryVectorIndex.js.map +1 -1
- package/package.json +26 -4
- package/rules/brain-rules.md +1 -1
- package/skills/create-package/SKILL.md +3 -3
- package/skills/create-package/references/config-and-credentials.md +1 -1
- package/skills/create-package/references/runtime-and-build.md +1 -1
- package/skills/create-package/references/skills-and-context.md +25 -12
- package/skills/create-package/references/test-and-ship.md +9 -16
- package/skills/create-package/scripts/verify-loaded.sh +18 -27
- package/skills/git/SKILL.md +27 -29
- package/skills/manage-files/SKILL.md +37 -32
- package/skills/manage-files/scripts/upload.sh +7 -9
- package/skills/manage-files/scripts/write.sh +2 -6
- package/skills/manage-sources/SKILL.md +93 -95
- package/skills/manage-sources/scripts/reembed.sh +2 -6
- package/skills/manage-uri-policy/SKILL.md +30 -58
- package/skills/git/scripts/git.sh +0 -48
- package/skills/manage-files/scripts/approve.sh +0 -9
- package/skills/manage-files/scripts/list.sh +0 -9
- package/skills/manage-files/scripts/pending.sh +0 -23
- package/skills/manage-files/scripts/raw.sh +0 -11
- package/skills/manage-files/scripts/read.sh +0 -9
- package/skills/manage-files/scripts/search.sh +0 -15
- package/skills/manage-sources/scripts/browse.sh +0 -12
- package/skills/manage-sources/scripts/cancel.sh +0 -14
- package/skills/manage-sources/scripts/create.sh +0 -13
- package/skills/manage-sources/scripts/delete.sh +0 -17
- package/skills/manage-sources/scripts/discoverable.sh +0 -6
- package/skills/manage-sources/scripts/get.sh +0 -7
- package/skills/manage-sources/scripts/kind-schema.sh +0 -7
- package/skills/manage-sources/scripts/kinds.sh +0 -6
- package/skills/manage-sources/scripts/list.sh +0 -6
- package/skills/manage-sources/scripts/purge-index.sh +0 -18
- package/skills/manage-sources/scripts/reindex.sh +0 -13
- package/skills/manage-sources/scripts/reset.sh +0 -11
- package/skills/manage-sources/scripts/set-enabled.sh +0 -17
- package/skills/manage-sources/scripts/sync-status.sh +0 -14
- package/skills/manage-sources/scripts/sync.sh +0 -13
- package/skills/manage-sources/scripts/update.sh +0 -18
- package/skills/manage-uri-policy/scripts/manifests.sh +0 -20
- package/skills/manage-uri-policy/scripts/set-uri.sh +0 -66
- package/skills/manage-uri-policy/scripts/show-uri.sh +0 -23
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
# only what the platform said.
|
|
10
10
|
#
|
|
11
11
|
# WHAT IT CHECKS
|
|
12
|
-
# Reads the agent-core package diagnostics and overview surfaces as YOU
|
|
13
|
-
# the session ticket
|
|
12
|
+
# Reads the agent-core package diagnostics and overview surfaces as YOU through
|
|
13
|
+
# `neuralis request` (the session ticket inside an activated skill, your
|
|
14
|
+
# `neuralis login` on your own machine), and reports one of:
|
|
14
15
|
# loaded — registered and operational
|
|
15
16
|
# partial — declarative contributions registered, no code runtime; prints
|
|
16
17
|
# the reason (build-missing | runtime-unavailable |
|
|
@@ -52,7 +53,7 @@
|
|
|
52
53
|
# 1 = partial (reason printed)
|
|
53
54
|
# 2 = absent — not listed for this caller
|
|
54
55
|
# 3 = error / missing (recorded message printed)
|
|
55
|
-
# 4 = could not reach the diagnostics surface (no
|
|
56
|
+
# 4 = could not reach the diagnostics surface (no `neuralis` CLI or login, no
|
|
56
57
|
# network, HTTP failure, or the caller lacks the `core.observe`
|
|
57
58
|
# feature). NOTHING is claimed about the package in this state.
|
|
58
59
|
#
|
|
@@ -127,8 +128,8 @@ if [ -z "$PKG_ID" ]; then
|
|
|
127
128
|
echo "usage: bash verify-loaded.sh <package-id> [--raw]" >&2
|
|
128
129
|
exit 4
|
|
129
130
|
fi
|
|
130
|
-
if ! command -v
|
|
131
|
-
echo "ERROR
|
|
131
|
+
if ! command -v neuralis >/dev/null 2>&1; then
|
|
132
|
+
echo "ERROR neuralis (the CLI) not found on PATH" >&2
|
|
132
133
|
manual_fallback
|
|
133
134
|
exit 4
|
|
134
135
|
fi
|
|
@@ -137,30 +138,20 @@ if ! command -v node >/dev/null 2>&1; then
|
|
|
137
138
|
manual_fallback
|
|
138
139
|
exit 4
|
|
139
140
|
fi
|
|
140
|
-
|
|
141
|
-
echo "ERROR NEURALIS_API is not set — activate this skill first (execute action=\"skill\"); the API base and the session ticket are injected at activation" >&2
|
|
142
|
-
manual_fallback
|
|
143
|
-
exit 4
|
|
144
|
-
fi
|
|
145
|
-
if [ -z "${NEURALIS_SESSION_TOKEN:-}" ]; then
|
|
146
|
-
echo "ERROR NEURALIS_SESSION_TOKEN is not set — activate this skill first; every internal call authenticates with the per-stream session ticket" >&2
|
|
147
|
-
manual_fallback
|
|
148
|
-
exit 4
|
|
149
|
-
fi
|
|
150
|
-
|
|
151
|
-
API_BASE="${NEURALIS_API%/}"
|
|
152
|
-
BASE_ROUTE="${API_BASE}/api/packages/@neuralis/agent-core/packages"
|
|
141
|
+
BASE_ROUTE="@neuralis/agent-core/packages"
|
|
153
142
|
|
|
154
143
|
WORK="$(mktemp -d)"
|
|
155
144
|
trap 'rm -rf "$WORK"' EXIT
|
|
156
145
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
[
|
|
146
|
+
# `neuralis request` prints a 2xx body on stdout (exit 0); a non-2xx body goes to
|
|
147
|
+
# stderr with an `HTTP <code>` line (exit 1). No such line = no answer
|
|
148
|
+
# at all (no CLI login, no network) — "000". Its error text stays in request.err.
|
|
149
|
+
fetch() { # fetch <route> <outfile> -> echoes the HTTP status ("000" = no response)
|
|
150
|
+
local rc=0 code
|
|
151
|
+
neuralis request GET "$1" >"$2" 2>"$WORK/request.err" || rc=$?
|
|
152
|
+
if [ "$rc" -eq 0 ]; then echo 200; return; fi
|
|
153
|
+
code="$(sed -n '/^HTTP [0-9][0-9][0-9]$/{s/^HTTP //p;q;}' "$WORK/request.err")"
|
|
154
|
+
if [ -n "$code" ]; then awk '!d && /^HTTP [0-9][0-9][0-9]$/ {d=1; next} {print}' "$WORK/request.err" > "$2"; else code="000"; fi
|
|
164
155
|
echo "$code"
|
|
165
156
|
}
|
|
166
157
|
|
|
@@ -174,7 +165,7 @@ if [ "$DIAG_CODE" = "403" ]; then
|
|
|
174
165
|
fi
|
|
175
166
|
if [ "$DIAG_CODE" != "200" ]; then
|
|
176
167
|
echo "ERROR GET ${BASE_ROUTE}/diagnostics returned HTTP ${DIAG_CODE}" >&2
|
|
177
|
-
[ -s "$WORK/
|
|
168
|
+
[ -s "$WORK/request.err" ] && sed 's/^/ /' "$WORK/request.err" >&2
|
|
178
169
|
[ -s "$WORK/diag.json" ] && head -c 600 "$WORK/diag.json" >&2 && echo >&2
|
|
179
170
|
manual_fallback
|
|
180
171
|
exit 4
|
|
@@ -322,7 +313,7 @@ const counts = () => {
|
|
|
322
313
|
process.stdout.write(' drop — and into NO tenant at all when the package is neither\n');
|
|
323
314
|
process.stdout.write(' first-party nor owned by a resolvable project, which is silent\n');
|
|
324
315
|
process.stdout.write(' apart from one server log line. Prove the instance exists with\n');
|
|
325
|
-
process.stdout.write(' GET
|
|
316
|
+
process.stdout.write(' neuralis request GET @neuralis/agent-core/connectors; that list is\n');
|
|
326
317
|
process.stdout.write(' the platform lane plus your own project, and nothing else.\n');
|
|
327
318
|
}
|
|
328
319
|
if (overEntry.enabled === false) {
|
package/skills/git/SKILL.md
CHANGED
|
@@ -14,28 +14,32 @@ panel drives**, so an agent gets exactly the rights a human member has: gated by
|
|
|
14
14
|
before every destructive op** and **credential-safe remotes** (the push/pull token is
|
|
15
15
|
resolved server-side and never touches the agent shell).
|
|
16
16
|
|
|
17
|
-
Call them with
|
|
18
|
-
|
|
19
|
-
`
|
|
20
|
-
|
|
17
|
+
Call them with `neuralis request <GET|POST> @neuralis/brain-core/git/<route> …` — inside an
|
|
18
|
+
activated skill it authenticates with the per-stream session ticket, on your own machine with
|
|
19
|
+
`neuralis login`; never with a git credential (fetch/pull/push resolve the remote token
|
|
20
|
+
server-side). The method is part of every call line: `commit` and `branch` READ on GET and
|
|
21
|
+
WRITE on POST. This is *not* about committing to the Neuralis repo itself — it is about an
|
|
22
|
+
agent operating git on a user's source.
|
|
21
23
|
|
|
22
24
|
## Three ways to reach git — pick the plane
|
|
23
25
|
|
|
24
26
|
| Plane | Use it for | How | Guarantees |
|
|
25
27
|
|---|---|---|---|
|
|
26
|
-
| **A. Governed routes** (prefer for local ops + your own remote) | Review, stage, commit, branch/checkout, and fetch/pull/push on a git-backed source in the workspace | `
|
|
28
|
+
| **A. Governed routes** (prefer for local ops + your own remote) | Review, stage, commit, branch/checkout, and fetch/pull/push on a git-backed source in the workspace | `neuralis request <GET|POST> @neuralis/brain-core/git/<route>` (this skill) | uri-policy gated; auto-snapshot on destructive ops; remote token resolved server-side, never in the shell |
|
|
27
29
|
| **B. External GitHub MCP** | Collaboration on the hosting platform — pull requests, issues, reviews, releases | the connected GitHub MCP server's tools (if the user connected one) | authz delegated to the GitHub account + enterprise settings — Neuralis does not re-implement git ACLs |
|
|
28
30
|
| **C. Raw git CLI** | Quick local inspection, or a user's own imported git skill | `execute action="shell"` running `git …` | uri-policy + Landlock sandboxed; secrets are stripped from the env, so it **cannot push**, and there is **no server snapshot**. It also resolves the repository by walking UP from `cwd`, so it is **repository-scoped, never source-scoped** — see the `repo` parameter note below |
|
|
29
31
|
|
|
30
32
|
Default to **A** for governed local ops and for pushing/pulling your connected remote; **B**
|
|
31
33
|
for platform collaboration; **C** for a fast read or when a user's own skill already drives git.
|
|
32
|
-
Plane A is always "call the routes
|
|
34
|
+
Plane A is always "call the routes with `neuralis request`" — the first-party `git` connector shown in the
|
|
33
35
|
`<packages>` overview is metadata; there are no first-party git MCP *tools* to call.
|
|
34
36
|
|
|
35
37
|
## Routes covered
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
Path: `@neuralis/brain-core/git/<route>`. GET carries `source` (+ optional `repo`) as `-f`
|
|
40
|
+
query fields; POST carries them in the JSON body (`--input -`), and every POST body must include
|
|
41
|
+
`"source"` plus that route's own required fields (e.g. `paths[]` for `stage`, `message` for
|
|
42
|
+
`commit`) — a source-less body is a `400`. `repo` is the optional in-source
|
|
39
43
|
selector for a nested checkout when the source root is not itself the repo (omit for a
|
|
40
44
|
single-repo source).
|
|
41
45
|
|
|
@@ -58,8 +62,10 @@ with nothing to report**. The same empty answer covers a
|
|
|
58
62
|
source with no `.git` at all. (`commit`, `outgoing` and `show` are honest — they answer a typed
|
|
59
63
|
`{"ok":false,"code":"unavailable"}` — and `diff` returns an empty diff string, except when it
|
|
60
64
|
refuses a huge read with its own typed `{"ok":false,"code":"too_large"}`. Both of those typed
|
|
61
|
-
refusals arrive on a **200**, so `
|
|
62
|
-
a non-zero exit is an HTTP refusal
|
|
65
|
+
refusals arrive on a **200**, so `neuralis request` exits 0: a zero exit with `ok:false` is a
|
|
66
|
+
soft answer, a non-zero exit is an HTTP refusal whose body and `HTTP <code>` are on stderr —
|
|
67
|
+
EVERY refusal carries `error`; a write-op failure and the three remote 409s also carry
|
|
68
|
+
`ok:false` and `code`; a 403 carries `code` (`feature_denied` / `uri_policy_denied`).)
|
|
63
69
|
|
|
64
70
|
**So an empty read is never evidence of a clean tree.** Do not test it with `branch:null`: a real
|
|
65
71
|
repository on a detached HEAD, or freshly initialised with no commit yet, reports exactly that.
|
|
@@ -112,7 +118,7 @@ Notes:
|
|
|
112
118
|
`no_credential` / `no_https_remote` otherwise).
|
|
113
119
|
- Destructive routes (`discard`, `branch` delete) snapshot server-side **before** mutating and
|
|
114
120
|
return a typed `rescue` carrier. **Undo it through the governed `restore` route** — echo the
|
|
115
|
-
carrier straight back: `POST restore
|
|
121
|
+
carrier straight back: `POST restore` with `{"source":"…","rescue":{"kind":"stash","ref":"<sha>"}}`
|
|
116
122
|
for discarded content, `{"kind":"branch","ref":"<anchor>","name":"<branch>"}` for a deleted
|
|
117
123
|
branch. Same gate as any other write (`drive.write` + `write` on the root — no `exec`, no new
|
|
118
124
|
capability), and the restore itself snapshots first. Do **not** reach for raw-shell
|
|
@@ -153,21 +159,13 @@ connect the remote in that card first.
|
|
|
153
159
|
general "don't `reset --hard` / `--no-verify` / force-push unrequested" rule still applies to
|
|
154
160
|
everything not named here.
|
|
155
161
|
|
|
156
|
-
## Env vars
|
|
157
|
-
|
|
158
|
-
- `NEURALIS_API` + `NEURALIS_SESSION_TOKEN` — injected at activation; every `git.sh` call reads them.
|
|
159
|
-
- `git.sh` arguments: `<GET|POST> <route>` then, for **GET**, URL-encoded `key=value` pairs; for
|
|
160
|
-
**POST**, a single JSON body string. Every POST body must include `"source"` plus that route's
|
|
161
|
-
own required fields (e.g. `paths[]` for `stage`, `message` for `commit`) — a source-less body is
|
|
162
|
-
a `400`.
|
|
163
|
-
|
|
164
162
|
## Examples
|
|
165
163
|
|
|
166
164
|
```bash
|
|
167
165
|
# Review: what changed, then the diff of one file, then recent history
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
neuralis request GET @neuralis/brain-core/git/status -f source=my-repo
|
|
167
|
+
neuralis request GET @neuralis/brain-core/git/diff -f source=my-repo -f path=src/app.ts
|
|
168
|
+
neuralis request GET @neuralis/brain-core/git/log -f source=my-repo -f maxCount=10
|
|
171
169
|
|
|
172
170
|
# NOT DISPOSABLE — every POST below writes to a REAL repository. These routes only
|
|
173
171
|
# operate on an already-attached git-backed source, so there is no throwaway repo to
|
|
@@ -181,16 +179,16 @@ bash ${SKILL_DIR}/scripts/git.sh GET log source=my-repo maxCount=10
|
|
|
181
179
|
# authorship, and is unsafe once it has been pushed (the remote keeps the original
|
|
182
180
|
# and the two histories diverge). Stage NAMED paths only — `paths:[…]` is what keeps
|
|
183
181
|
# a stray secret from being swept in; there is no route that un-commits.
|
|
184
|
-
|
|
185
|
-
|
|
182
|
+
neuralis request POST @neuralis/brain-core/git/stage --input - <<<'{"source":"my-repo","paths":["src/app.ts","README.md"]}'
|
|
183
|
+
neuralis request POST @neuralis/brain-core/git/commit --input - <<<'{"source":"my-repo","message":"Fix null guard in loader"}'
|
|
186
184
|
|
|
187
185
|
# Create a branch, then switch to it.
|
|
188
186
|
# checkout MOVES HEAD and swaps the working tree to that branch's content — the files
|
|
189
187
|
# the user is looking at change under them. It is plain (never `-f`): if the switch
|
|
190
188
|
# would overwrite uncommitted local changes, git refuses and you get a 409. Report
|
|
191
189
|
# that refusal; there is no force option here and you must not build one in Plane C.
|
|
192
|
-
|
|
193
|
-
|
|
190
|
+
neuralis request POST @neuralis/brain-core/git/branch --input - <<<'{"source":"my-repo","action":"create","name":"fix/loader"}'
|
|
191
|
+
neuralis request POST @neuralis/brain-core/git/checkout -f source=my-repo -f ref=fix/loader
|
|
194
192
|
|
|
195
193
|
# Sync a connected remote (needs uri-policy exec + a connected remote).
|
|
196
194
|
# pull REWRITES the working tree to the remote tip — fast-forward only, so it refuses
|
|
@@ -200,9 +198,9 @@ bash ${SKILL_DIR}/scripts/git.sh POST checkout '{"source":"my-repo","ref":"fix/l
|
|
|
200
198
|
# OUTWARD-FACING: teammates, CI, and anything deploying from that branch see it the
|
|
201
199
|
# moment it lands, and nothing in Neuralis can take it back. ASK THE USER FIRST, every
|
|
202
200
|
# time — this is the one line here you never run on your own initiative.
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
neuralis request POST @neuralis/brain-core/git/pull -f source=my-repo
|
|
202
|
+
neuralis request POST @neuralis/brain-core/git/push -f source=my-repo
|
|
205
203
|
|
|
206
204
|
# Nested repo inside a multi-repo source: add the repo selector
|
|
207
|
-
|
|
205
|
+
neuralis request GET @neuralis/brain-core/git/status -f source=mono -f repo=packages/api
|
|
208
206
|
```
|
|
@@ -7,24 +7,25 @@ allowed-tools: execute
|
|
|
7
7
|
|
|
8
8
|
# Files Management Skill
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
`
|
|
10
|
+
Covers `@neuralis/brain-core/{list,search,read,raw,write,upload,approve,pending}`.
|
|
11
|
+
The `fs_*` tools (`fs_read`, `fs_write`, etc.) remain the primary path for one-off
|
|
12
|
+
file operations — this skill is for *bulk*: iterating with a shell loop, posting a
|
|
13
|
+
batch, or scripting an `upload + approve` sequence end-to-end. Call the routes with
|
|
14
|
+
`neuralis request` (the session ticket inside an activated skill, `neuralis login`
|
|
15
|
+
on your own machine); `write` and `upload` go through their scripts.
|
|
15
16
|
|
|
16
17
|
## Routes covered
|
|
17
18
|
|
|
18
|
-
| Action | Method | Path
|
|
19
|
-
|
|
20
|
-
| List files / folders at a URI | POST |
|
|
21
|
-
| Search vector index | POST |
|
|
22
|
-
| Read multiple files | POST |
|
|
23
|
-
| Raw (un-summarized) read | GET |
|
|
24
|
-
| Write — every verb (create / overwrite / edit / insert / lines / fields / move / copy / mkdir / revert / delete) | POST |
|
|
25
|
-
| Upload a payload (multipart/form-data) | POST |
|
|
26
|
-
| Approve a pending change | POST |
|
|
27
|
-
| List pending changes (project-wide) | GET |
|
|
19
|
+
| Action | Method | Path (`@neuralis/brain-core/…`) | Feature gate |
|
|
20
|
+
|---|---|---|---|
|
|
21
|
+
| List files / folders at a URI | POST | `list` | `drive.read` |
|
|
22
|
+
| Search vector index | POST | `search` | `drive.search` |
|
|
23
|
+
| Read multiple files | POST | `read` | `drive.read` |
|
|
24
|
+
| Raw (un-summarized) read | GET | `raw` | `drive.read` |
|
|
25
|
+
| Write — every verb (create / overwrite / edit / insert / lines / fields / move / copy / mkdir / revert / delete) | POST | `write` — `scripts/write.sh` | `drive.write` |
|
|
26
|
+
| Upload a payload (multipart/form-data) | POST | `upload` — `scripts/upload.sh` | `drive.write` |
|
|
27
|
+
| Approve a pending change | POST | `approve` | `drive.write` |
|
|
28
|
+
| List pending changes (project-wide) | GET | `pending` | `drive.read` |
|
|
28
29
|
|
|
29
30
|
Per-route feature gates are enforced server-side by the RouteDispatcher against the
|
|
30
31
|
caller's real `grantedFeatures`, independently of this skill's `requiredFeatures`
|
|
@@ -32,41 +33,45 @@ frontmatter — `/list`, `/read`, `/raw` and `GET /pending` need `drive.read`; `
|
|
|
32
33
|
`drive.search`; `/write`, `/upload`, `/approve` and the mutating `/pending`
|
|
33
34
|
verbs (`revert`, `discard`, `dismiss`, `discard-orphans`) need `drive.write`. A caller who holds
|
|
34
35
|
`drive.write` (enough to see and activate this skill) but NOT `drive.search` gets a
|
|
35
|
-
server-side **403** from `search
|
|
36
|
+
server-side **403** from `search` even though the other routes work.
|
|
36
37
|
|
|
37
|
-
##
|
|
38
|
+
## Bodies
|
|
38
39
|
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
- `/write` body = `{"agentId":"…","uri":"data://notes/plan.md","<verb>":…}` — the VERB is a KEY and its presence is the intent. Content verbs COMPOSE in one call, in a fixed order, as ONE write with ONE baseline: `create` (`{content, client_request_id?}` — never overwrites; a taken name lands BESIDE as `name-2.ext` and the response `uri` is where it went, so read it back), `overwrite` (`{content}` — refuses on a missing file and names `create`), `edit` (`[{old_string, new_string?, replace_all?, expected_replacements?}]`, applied in order, one failure discards all of them), `insert` (`{insert_text, insert_line?}` — inserts AFTER that line, `0` = start of file, omitted = append), `lines` (`{line_start, line_end, content}`), `fields` (`{category, status, pinned, tags{add,remove,set}, metadata}`). Structural verbs stand ALONE: `move`/`copy` (a destination uri or, for a file move, a bare new filename; `copy` may cross sources), `mkdir:true` (a real empty directory; not on `brain://`, where folders are implicit), `revert:true` (one-level undo), `delete:true` (a file, or a whole folder recursively). **A 2xx does not mean success** — a refusal is 200 with `ok:false`, so branch on `ok`. A `move` (or a `revert` that relocates or re-creates) onto a uri that already holds something answers `ok:false` + `error:"DESTINATION_EXISTS"` — nothing was overwritten; delete or rename the occupant first. A delete has ONE spelling, the `delete` key: `fields.status:"deleted"` is refused. `review` is never read from the body — a content edit (`overwrite`/`edit`/`insert`/`lines`) lands `pending_review` on every lane (visible and revertible in Changes); `create`/`move`/`copy`/`mkdir` land committed for a human interactive caller and `pending_review` from this ticket lane; `/upload` also journals agent-origin uploads as pending review; interactive human uploads land committed. Use `/write` for JSON text and `/upload` for multipart file bytes. A missing `agentId`/`uri` is a **400** naming the field, a body the server could not parse a **400** `Request body must be a JSON object`. A uri-policy denial is a **403 with no `ok` field**, a disabled source a **409** with `code:"source_disabled"`. A source slug this project does not have answers one of two ways, and BOTH mean "stop guessing this name": with a scheme (`zzz://a.md`) the uri gate answers **403 `uri_policy_denied`** first; without one (`notes.md`) you get **404 `code:"source_not_configured"`** carrying `availableSources` — the sources you CAN address. `/list` gives the 404 in both forms. Switch to a source from that list (or from `<runtime_stack>`); retrying the same name never works. `/read` is different again: it reports PER FILE inside a 200, so a denied or disabled source arrives as `{uri, error, code:"forbidden"}` — never a 409 — and a missing file as `code:"not_found"`. No code means the read failed for some other reason; do not conclude the file is gone. A folder delete or copy that partly succeeded reports what really happened: `affected` (files touched) and `failed` (the NAMES that could not be), so "failed" does not mean "nothing happened". `warning:"moved_with_source_retained"` on a move means the copy landed but the original could not be removed — BOTH copies exist on purpose; do not re-run the move. `indexed:false` means the bytes DID land and are readable, but search indexing was skipped or failed; report it, do not re-write. Sync retries failed indexing only for eligible files; excluded files remain unindexed.
|
|
40
|
+
- `list` / `search` / `read` / `approve` take a JSON body (`--input - <<<'<json>'`);
|
|
41
|
+
`write.sh` takes it as `BODY_JSON`. `create.sh`, `modify.sh`, `copy.sh` and `mkdir.sh` are kept only so an imported skill's existing path still writes; they exec `write.sh`, so they take the same verb-keyed body.
|
|
42
|
+
- `/write` body = `{"agentId":"…","uri":"data://notes/plan.md","<verb>":…}` — the VERB is a KEY and its presence is the intent. Content verbs COMPOSE in one call, in a fixed order, as ONE write with ONE baseline: `create` (`{content, client_request_id?}` — never overwrites; a taken name lands BESIDE as `name-2.ext` and the response `uri` is where it went, so read it back), `overwrite` (`{content}` — refuses on a missing file and names `create`), `edit` (`[{old_string, new_string?, replace_all?, expected_replacements?}]`, applied in order, one failure discards all of them), `insert` (`{insert_text, insert_line?}` — inserts AFTER that line, `0` = start of file, omitted = append), `lines` (`{line_start, line_end, content}`), `fields` (`{category, status, pinned, tags{add,remove,set}, metadata}`). Structural verbs stand ALONE: `move`/`copy` (a destination uri or, for a file move, a bare new filename; `copy` may cross sources), `mkdir:true` (a real empty directory; not on `brain://`, where folders are implicit), `revert:true` (one-level undo), `delete:true` (a file, or a whole folder recursively). **A 2xx does not mean success** — a refusal is 200 with `ok:false`, so branch on `ok`. A `move` (or a `revert` that relocates or re-creates) onto a uri that already holds something answers `ok:false` + `error:"DESTINATION_EXISTS"` — nothing was overwritten; delete or rename the occupant first. A delete has ONE spelling, the `delete` key: `fields.status:"deleted"` is refused. `review` is never read from the body — a content edit (`overwrite`/`edit`/`insert`/`lines`) lands `pending_review` on every lane (visible and revertible in Changes); `create`/`move`/`copy`/`mkdir` land committed for a human interactive caller and `pending_review` from this ticket lane; `/upload` also journals agent-origin uploads as pending review; interactive human uploads land committed. Use `/write` for JSON text and `/upload` for multipart file bytes. A missing `agentId`/`uri` is a **400** naming the field, a body the server could not parse a **400** `Request body must be a JSON object`. A uri-policy denial is a **403 with no `ok` field**, a disabled source a **409** with `code:"source_disabled"`. A source slug this project does not have answers one of two ways, and BOTH mean "stop guessing this name": with a scheme (`zzz://a.md`) the uri gate answers **403 `uri_policy_denied`** first; without one (`notes.md`) you get **404 `code:"source_not_configured"`** carrying `availableSources` — the sources you CAN address. `/list` gives the 404 in both forms. Switch to a source from that list (or from `<runtime_stack>`); retrying the same name never works. `/read` is different again: it reports PER FILE inside a 200, so a denied or disabled source arrives as `{uri, error, code:"forbidden"}` — never a 409 — and a missing file as `code:"not_found"`. No code means the read failed for some other reason; do not conclude the file is gone. A folder delete or copy that partly succeeded reports what really happened: `affected` (files touched) and `failed` (the NAMES that could not be), so "failed" does not mean "nothing happened". `warning:"moved_with_source_retained"` on a move means the copy landed but the original could not be removed — BOTH copies exist on purpose; do not re-run the move. `indexed:false` means the bytes DID land and are readable, but search indexing was skipped or failed; report it, do not re-write. Sync retries failed indexing only for eligible files; excluded files remain unindexed. A non-2xx prints the route's body too — on stderr, followed by an `HTTP <code>` line, under a `--- stderr ---` separator, so read the whole output.
|
|
42
43
|
- `/search` body key is `search_mode` (semantic|filter|grep) — the SAME vocabulary as the `fs_search` tool; the legacy `mode` key is rejected with 400. The route forwards `search_mode`, `query`, `folder_uri`, `glob`, `category`, `tags`, `status`, `created_after`/`created_before`, `limit`, and the grep-only `path_glob`, `context` and `case_sensitive`. In `grep` mode the search is uri-policy `read`-gated (only paths the caller holds `read` policy on are grepped), the match is a FIXED STRING, and the answer carries `truncated` + `total_seen` — a cut set says so, so never read a full-looking list as proof that the rest is absent.
|
|
43
|
-
- `/raw` is a GET — pass the file URI
|
|
44
|
+
- `/raw` is a GET — pass the file URI as `-f uri=<uri>` and redirect stdout to a file: it always returns the full bytes, unaffected by the response headers; what those headers say is a BROWSER instruction. Safe types (images except SVG, video, audio, PDF, text, JSON) are marked for inline display; SVG, HTML and XML are marked `attachment` so a person clicking the link downloads them instead of executing them on the app origin. Do not describe a `.svg` or `.html` you wrote as viewable in the browser — offer it as a download, or convert it to a raster image if a human needs to look at it.
|
|
44
45
|
- `/read` is at parity with the `fs_read` tool: the body takes `files[]` — each entry a URI string or `{"uri":"…","line_start":N,"line_end":M}` with that file's own window — plus `read_version`, batch-level `line_start`/`line_end` and `include_ids`. An entry that is neither shape is a **400**. Text is a bounded preview; native media may accompany it, while unsupported/invalid binary data has a URI and marker. For a byte-exact binary copy use the authorized `/raw` byte lane and multipart `/upload`, never re-encode a text preview. If you only need the pre-edit content, a plain `/read` response already carries it: each file's `version.baseline.content`, with `available_versions` listing `baseline` exactly when one exists.
|
|
45
|
-
- `/upload` is **multipart/form-data**, NOT JSON —
|
|
46
|
-
- `GET /pending` lists durable pending records inside the Changes workspace. `
|
|
46
|
+
- `/upload` is **multipart/form-data**, NOT JSON — `upload.sh` takes `FILE_PATH=…` `AGENT_ID=…` (required) plus optional `URI=…` / `TAGS=a,b`; a JSON body is rejected with 400. The 200 carries `indexed`: `false` means the bytes DID land but search indexing was skipped by the source selection or failed (provider outage/quota) — report it, do not re-upload. An eligible sync retries failed indexing; excluded uploads remain unindexed until the selection changes. The 200 also carries `mimeType`, and it may differ from what the upload declared: the stored type is resolved from the filename extension first, with the declared value kept only for an extension the platform does not know, and a common-image claim the leading bytes contradict falls back to `application/octet-stream`. Give the file a truthful extension and the stored type follows.
|
|
47
|
+
- `GET /pending` lists durable pending records inside the Changes workspace. `status` (`pending_review`\|`approved`\|`reverted`), `source`, `op` (`create`\|`modify`\|`delete`\|`move`) and `limit` filter the list; owner/scope and URI READ gates apply per record. Review begins with `POST /pending` `{action:"preview",id}`: it returns the current connector content separately from the recorded snapshot and saved baseline, typed missing/unavailable/binary/too-large states, `indexState`, `requiresAcknowledgement`, and `expectedFingerprint`. The preview is bounded by `pendingPreviewMaxBytes`; it never fabricates missing content or vectors. Resolve through `POST /approve` `{id,expectedFingerprint}` or `POST /pending` `{action:"revert"|"dismiss",id,expectedFingerprint}`. If the preview requires acknowledgement, inspect those states and include `acknowledgeCurrentState:true`; never auto-acknowledge an unseen preview. The server rechecks freshness and WRITE authority at apply: a changed fingerprint requires a new preview, and acknowledgement grants no missing permission. Bare legacy `/write revert:true` refuses unsafe pending recovery; ordinary non-pending one-level undo remains available. Approval/dismissal preserve the stored snapshot/index state (including recovery-only or absent index), rather than certifying current connector bytes as indexed. For a file the preview shows `missing` with a saved `journal-latest-write` baseline, an acknowledged approve/dismiss closes the review and the file stays gone; revert is the restore. Terminal retries remain owner-gated and idempotent; `uri` is omitted without current READ and WRITE access. Revert restores the saved baseline and refuses missing baselines or occupied move destinations. Discard/Repair remain orphan-only, owner-gated operations; no transient outage is proof of orphanhood.
|
|
47
48
|
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
`/api/packages/@neuralis/brain-core/list,search,read,write,…` route source
|
|
51
|
-
for the exact schemas. For one-off calls, prefer the `fs_*` tools.
|
|
50
|
+
For one-off calls, prefer the `fs_*` tools.
|
|
52
51
|
|
|
53
52
|
## Examples
|
|
54
53
|
|
|
55
54
|
For a disposable pending record you created in your project, substitute its real id below. Inspect the preview before resolving; copy its exact fingerprint, and add acknowledgement only after reviewing the states the preview names. A 409 requires a new preview, never a fabricated fingerprint.
|
|
56
55
|
|
|
57
56
|
```bash
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
neuralis request POST @neuralis/brain-core/pending -f action=preview -f id=<scratch-record-id>
|
|
58
|
+
neuralis request POST @neuralis/brain-core/pending --input - <<<'{"action":"revert","id":"<scratch-record-id>","expectedFingerprint":"<64-hex-preview-fingerprint>","acknowledgeCurrentState":true}'
|
|
60
59
|
```
|
|
61
60
|
|
|
61
|
+
Pending calls return one page; scan-orphans preserves that page in `changes` and puts its orphan subset in `orphans`/`orphanIds`: pass `cursor` from `nextCursor` unchanged with the same filters, or `uri` for an exact URI. Check `complete`, `partial` and `hasMoreAvailable`; a partial exhausted branch remains incomplete. On `restartRequired`, restart the list instead of retrying the old cursor. Repair returns scannedVisible/orphaned/attempted/discarded/refused/failed/reasons for one batch; continue only via nextCursor; pause on partial and use an explicit Continue available/Retry.
|
|
62
|
+
|
|
62
63
|
```bash
|
|
63
64
|
# Recursive list of /data
|
|
64
|
-
|
|
65
|
-
bash ${SKILL_DIR}/scripts/list.sh
|
|
65
|
+
neuralis request POST @neuralis/brain-core/list --input - <<<'{"uri":"data://","recursive":true,"limit":100}'
|
|
66
66
|
|
|
67
67
|
# Vector search across all sources (body key is `search_mode`, same as the fs_search tool)
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
neuralis request POST @neuralis/brain-core/search -f search_mode=semantic -f query='telemetry pipeline' -F limit=10
|
|
69
|
+
|
|
70
|
+
# Pending review records still waiting, one page
|
|
71
|
+
neuralis request GET @neuralis/brain-core/pending -f status=pending_review -f limit=20
|
|
72
|
+
|
|
73
|
+
# The exact bytes of one file
|
|
74
|
+
neuralis request GET @neuralis/brain-core/raw -f uri=data://report.pdf > /tmp/report.pdf
|
|
70
75
|
|
|
71
76
|
# Create a text file — the journaled write lane. The verb is the KEY.
|
|
72
77
|
# `<agent-id>` is a placeholder; a real id makes this line write for real.
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
|
-
API_BASE="${NEURALIS_API:?NEURALIS_API not set; activate skill first}"
|
|
4
|
-
TOKEN="${NEURALIS_SESSION_TOKEN:?missing session ticket; activate the skill via execute action=\"skill\"}"
|
|
5
3
|
# POST /upload REQUIRES multipart/form-data (NOT a JSON body): the handler reads the
|
|
6
4
|
# form fields file (the binary/text payload), agentId, uri (optional), tags (optional,
|
|
7
5
|
# comma-split). projectId is taken from the session ticket — passing a different one
|
|
@@ -9,10 +7,10 @@ TOKEN="${NEURALIS_SESSION_TOKEN:?missing session ticket; activate the skill via
|
|
|
9
7
|
# from the form field.
|
|
10
8
|
FILE_PATH="${FILE_PATH:?FILE_PATH is required (local path of the payload to upload)}"
|
|
11
9
|
AGENT_ID="${AGENT_ID:?AGENT_ID is required (form field; the destination agent)}"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
${
|
|
18
|
-
|
|
10
|
+
# `--form` sends multipart/form-data (`file=@path` is the file part). A non-2xx
|
|
11
|
+
# answer prints the route's body and `HTTP <code>` on stderr and exits 1.
|
|
12
|
+
neuralis request POST @neuralis/brain-core/upload \
|
|
13
|
+
--form "file=@${FILE_PATH}" \
|
|
14
|
+
--form "agentId=${AGENT_ID}" \
|
|
15
|
+
${URI:+--form "uri=${URI}"} \
|
|
16
|
+
${TAGS:+--form "tags=${TAGS}"}
|
|
@@ -16,8 +16,6 @@ set -euo pipefail
|
|
|
16
16
|
# `edit`, `insert`, `lines`) lands `pending_review` on EVERY lane; `create`, `move`,
|
|
17
17
|
# `copy` and `mkdir` land committed for a human interactive caller and
|
|
18
18
|
# `pending_review` from this ticket lane; `delete` tombstones on both.
|
|
19
|
-
API_BASE="${NEURALIS_API:?NEURALIS_API not set; activate skill first}"
|
|
20
|
-
TOKEN="${NEURALIS_SESSION_TOKEN:?missing session ticket; activate the skill via execute action=\"skill\"}"
|
|
21
19
|
BODY_JSON="${BODY_JSON:?BODY_JSON is required — a JSON object with agentId, uri and ONE verb key; see the header comment for the shape}"
|
|
22
20
|
# The retired body shape names no verb; the route would only say "Missing uri".
|
|
23
21
|
# Refuse it HERE, naming what the caller has to send instead.
|
|
@@ -27,7 +25,5 @@ case "${BODY_JSON}" in
|
|
|
27
25
|
exit 2
|
|
28
26
|
;;
|
|
29
27
|
esac
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
-H "Content-Type: application/json" -d "${BODY_JSON}" \
|
|
33
|
-
"${API_BASE}/api/packages/@neuralis/brain-core/write"
|
|
28
|
+
# A non-2xx answer prints the route's body and `HTTP <code>` on stderr and exits 1.
|
|
29
|
+
neuralis request POST @neuralis/brain-core/write --input - <<<"${BODY_JSON}"
|