@neurosquad/card-sdk 1.0.0 → 1.2.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/CHANGELOG.md +140 -0
- package/README.md +38 -3
- package/dist/card-sdk.js +121 -5
- package/dist/cli.js +43 -2
- package/dist/react.js +1 -1
- package/dist/testing.js +71 -1
- package/dist/types/client/card.d.ts +19 -1
- package/dist/types/contract/api.d.ts +133 -0
- package/dist/types/contract/source.d.ts +2 -1
- package/dist/types/contract/verified.d.ts +136 -0
- package/dist/types/contract/version.d.ts +1 -1
- package/dist/types/testing/mockHost.d.ts +12 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@neurosquad/card-sdk` and to the card contract it speaks
|
|
4
|
+
(`CARD_SDK_CONTRACT_VERSION`) are listed here, newest first. The format follows
|
|
5
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the package uses
|
|
6
|
+
[Semantic Versioning](https://semver.org/).
|
|
7
|
+
|
|
8
|
+
Three versions matter to a card author:
|
|
9
|
+
|
|
10
|
+
- **SDK / contract version** (`SDK_VERSION`, `CARD_SDK_CONTRACT_VERSION`) — the
|
|
11
|
+
headings below. New methods and events arrive in minor versions.
|
|
12
|
+
- **Wire protocol** (`CARD_PROTOCOL_VERSION`, still **1**) — a card built for a
|
|
13
|
+
higher protocol is refused by an older app with `PROTOCOL_MISMATCH`.
|
|
14
|
+
- **NeuroSquad app version** — the host. Feature-detect with
|
|
15
|
+
`card.host.supports('<method>')`, or set `minAppVersion` in
|
|
16
|
+
`neurosquad-card.json` when the card cannot work without a method.
|
|
17
|
+
|
|
18
|
+
App versions below are the first public NeuroSquad release that has the change.
|
|
19
|
+
|
|
20
|
+
## [Unreleased]
|
|
21
|
+
|
|
22
|
+
### Changed (host, NeuroSquad 0.1.264)
|
|
23
|
+
|
|
24
|
+
- A card whose page process dies on its own (typically the machine running out
|
|
25
|
+
of memory) is reloaded automatically instead of staying a grey rectangle; the
|
|
26
|
+
new frame starts with `launch: 'reloaded'`. After three such reloads within
|
|
27
|
+
10 minutes the card shows "The card stopped working" with **Reload**.
|
|
28
|
+
Nothing to do for authors — keep state you need across reloads in
|
|
29
|
+
`card.storage`.
|
|
30
|
+
|
|
31
|
+
## [1.2.0] — 2026-10-04
|
|
32
|
+
|
|
33
|
+
Host: NeuroSquad **0.1.264** or newer (`card.host.supports('agents.timeline')`).
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- `card.agents.timeline(agentId, since, until?)` → `AgentTimeline`: one
|
|
38
|
+
arrow-connected AI agent's model requests (completion time; send time where
|
|
39
|
+
the harness's log records it; tokens; tool calls the model asked for), its
|
|
40
|
+
status as consecutive spans, and its turns — what the official Agent Pulse
|
|
41
|
+
card draws. `requestTimes` says what the log allows: `start-end`, `end` or
|
|
42
|
+
`none`. At most 2000 requests (the newest; `truncated`).
|
|
43
|
+
- Types `AgentTimeline`, `AgentRequestSpan`, `AgentTurnSpan`.
|
|
44
|
+
- Mock host: `agentTimeline` option and `setAgentTimeline(agentId, timeline)`.
|
|
45
|
+
|
|
46
|
+
### Compatibility
|
|
47
|
+
|
|
48
|
+
- Same permission and scope as `agents.usage`: `usage.read` and an arrow to an
|
|
49
|
+
AI agent. No new permission.
|
|
50
|
+
- Rate limit 60 calls a minute per card.
|
|
51
|
+
- An older app answers `METHOD_NOT_FOUND`.
|
|
52
|
+
|
|
53
|
+
## [1.1.0] — 2026-10-04
|
|
54
|
+
|
|
55
|
+
Host: NeuroSquad **0.1.264** or newer (`card.host.supports('agents.usage')`).
|
|
56
|
+
|
|
57
|
+
### Added
|
|
58
|
+
|
|
59
|
+
- `card.agents.usage(agentId, since, until?)` → `AgentUsage`: one
|
|
60
|
+
arrow-connected AI agent's run inside a window — model requests, tokens by
|
|
61
|
+
kind (input, output, reasoning, cache read, cache write, total), prompts,
|
|
62
|
+
working and elapsed time, cost in micro-dollars (rounded up) with
|
|
63
|
+
`costPartial`, model and provider. What the official Run Stats card shows.
|
|
64
|
+
Timing comes from the app's own status history, so it is right even while
|
|
65
|
+
the card's frame was paused.
|
|
66
|
+
- Type `AgentUsage`.
|
|
67
|
+
- Mock host: `agentUsage` option and `setAgentUsage(agentId, usage)`.
|
|
68
|
+
- Contract types for the verified cards catalog (`VerifiedCatalog`,
|
|
69
|
+
`VerifiedEntry`, `validateVerifiedCatalog`, …) — first published here; they
|
|
70
|
+
were added to the source after the 1.0.0 package.
|
|
71
|
+
|
|
72
|
+
### Changed
|
|
73
|
+
|
|
74
|
+
- A token count that is not reported is `null`, never `0`: a field the
|
|
75
|
+
harness's log does not carry, a harness without a readable usage log (Amp,
|
|
76
|
+
Cursor → `usageReadable: false`), a zero cache count from the user's own model
|
|
77
|
+
server, a zero cache write outside the Anthropic API. Show it as "not
|
|
78
|
+
reported".
|
|
79
|
+
- A cost of exactly 0 that a harness records for a model on the user's own
|
|
80
|
+
server means "no price": `costMicroUsd` is `null`, never $0.
|
|
81
|
+
- `prompts` does not count a finished turn without a single model request (a
|
|
82
|
+
status blip, such as a pasted prompt a CLI showed as busy before it really
|
|
83
|
+
submitted).
|
|
84
|
+
|
|
85
|
+
### Compatibility
|
|
86
|
+
|
|
87
|
+
- Permission `usage.read` (already in 1.0; it now also unlocks this method for
|
|
88
|
+
agents connected by an arrow). Not connected → `NOT_CONNECTED`; a shell →
|
|
89
|
+
`INVALID_PARAMS`.
|
|
90
|
+
- Rate limit 60 calls a minute per card.
|
|
91
|
+
- An older app answers `METHOD_NOT_FOUND`. Wire protocol stays 1; cards built
|
|
92
|
+
with 1.0.0 run unchanged.
|
|
93
|
+
|
|
94
|
+
### Host changes within contract 1.0 (no SDK version change)
|
|
95
|
+
|
|
96
|
+
Shipped in NeuroSquad releases between 1.0.0 and 1.1.0; listed because they can
|
|
97
|
+
change what a card sees.
|
|
98
|
+
|
|
99
|
+
- **0.1.253** — more manifest `name` values are reserved (the Graphify plugin's
|
|
100
|
+
tool family). **0.1.230** — reserved for the Memory, Context7 and Code Graph
|
|
101
|
+
plugins. **0.1.160** — for the RTK and caveman plugins. **0.1.141** —
|
|
102
|
+
`canvas_connect`, `canvas_disconnect`, `canvas_list`. **0.1.138** —
|
|
103
|
+
`agent_set_model`, `neurosquad_models`. A manifest with such a `name` fails
|
|
104
|
+
validation.
|
|
105
|
+
- **0.1.214** — `fs.*` refuses writes to more files that run code on the next
|
|
106
|
+
push or install: GitLab, Jenkins, CircleCI, Buildkite, Travis, Bitbucket,
|
|
107
|
+
Drone, AppVeyor and Azure Pipelines configs. `workspace.path` of a workspace
|
|
108
|
+
in WSL is this computer's view of it; for an SSH workspace it is `''` and
|
|
109
|
+
`fs.*` answers `UNAVAILABLE`. `usage.summary` rounds costs up to whole
|
|
110
|
+
micro-dollars, so a tiny priced cost never reads as 0. Linking a folder with
|
|
111
|
+
`neurosquad-card dev` needs developer mode on.
|
|
112
|
+
- **0.1.128** — `agents.lastReply` works for every harness with a readable
|
|
113
|
+
transcript, not only Claude Code, and an agent's built-in `reply` port
|
|
114
|
+
sends the final answer for all of them.
|
|
115
|
+
- **0.1.125** — the verified cards catalog (§18 of the spec): "Verified" badge
|
|
116
|
+
for cards listed at an exact commit and tree hash.
|
|
117
|
+
|
|
118
|
+
## [1.0.0] — 2026-09-25
|
|
119
|
+
|
|
120
|
+
Host: NeuroSquad **0.1.123** or newer. First public release.
|
|
121
|
+
|
|
122
|
+
### Added
|
|
123
|
+
|
|
124
|
+
- `connect()` → a typed `Card`: `card.call` / `card.on` over the whole
|
|
125
|
+
contract, plus namespaces for storage, settings, agents, terminals, ports,
|
|
126
|
+
tools for connected agents (MCP), network proxy, files, clipboard, usage
|
|
127
|
+
summary, theme, language, lifecycle and card UI (status, badge, overview,
|
|
128
|
+
attention, toasts, confirm).
|
|
129
|
+
- Manifest `neurosquad-card.json` (`manifestVersion: 1`) with its JSON Schema,
|
|
130
|
+
permissions with consent text, optional permissions requested at run time.
|
|
131
|
+
- React bindings (`@neurosquad/card-sdk/react`), the optional dark UI kit
|
|
132
|
+
(`ui.css`, `applyTheme()`), `createTranslator` for en/ru/zh.
|
|
133
|
+
- Mock host for tests (`@neurosquad/card-sdk/testing`).
|
|
134
|
+
- CLI `neurosquad-card`: `create` (vanilla and React templates), `validate`,
|
|
135
|
+
`pack`, `dev` (live reload into the running app).
|
|
136
|
+
|
|
137
|
+
[Unreleased]: https://docs.neurosquad.ai/en/card-sdk/changelog
|
|
138
|
+
[1.2.0]: https://docs.neurosquad.ai/en/card-sdk/changelog
|
|
139
|
+
[1.1.0]: https://docs.neurosquad.ai/en/card-sdk/changelog
|
|
140
|
+
[1.0.0]: https://www.npmjs.com/package/@neurosquad/card-sdk/v/1.0.0
|
package/README.md
CHANGED
|
@@ -180,7 +180,7 @@ subscribed with the host automatically while a listener exists.
|
|
|
180
180
|
| `card.ui` | `toast`, `confirm` → boolean, `setMenu([{ id, label, icon, onSelect }])`, `onMenu` |
|
|
181
181
|
| `card.storage` | `get(key, fallback)`, `set`, `delete`, `keys(prefix)`, `clear`, `usage`; `card.storage.package.*` shared by every card of the package; `onChange` |
|
|
182
182
|
| `card.settings` | `values`, `value(key)`, `hasSecret(key)`, `set(values)`, `open()`, `onChange` |
|
|
183
|
-
| `card.agents` | `list`, `get`, `readScreen`, `lastReply`, `prompt(id, text, { whenBusy, submit })`, `onStatus`, `onTurn`, `onChanged`, `onOutput(ids, handler)` |
|
|
183
|
+
| `card.agents` | `list`, `get`, `readScreen`, `lastReply`, `prompt(id, text, { whenBusy, submit })`, `usage(id, since, until?)`, `timeline(id, since, until?)`, `onStatus`, `onTurn`, `onChanged`, `onOutput(ids, handler)` |
|
|
184
184
|
| `card.terminals` | `run(id, command)` → `{ exitCode, output }`, `write(id, text, { submit })` |
|
|
185
185
|
| `card.ports` | `inputs`, `outputs`, `peers`, `emit(output, data)`, `send`, `request`, `read`, `onMessage`, `onRequest(input, handler)`, `onPeersChanged` |
|
|
186
186
|
| `card.tools` | `handle(name, handler)`, `setEnabled` |
|
|
@@ -238,6 +238,37 @@ await requestPermissions(card, ...needed.filter((id) => id !== null)) // never t
|
|
|
238
238
|
await card.ports.emit('summary', text)
|
|
239
239
|
```
|
|
240
240
|
|
|
241
|
+
### One agent's run: `card.agents.usage`
|
|
242
|
+
|
|
243
|
+
Since SDK 1.1 (NeuroSquad 0.1.264): the numbers of one AI agent connected to the
|
|
244
|
+
card by an arrow, inside a time window — model requests and tokens from the
|
|
245
|
+
harness's own log, prompts and working time from the app's status history (so
|
|
246
|
+
they are right for time your frame was off screen). Needs `usage.read`.
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
if (await card.host.supports('agents.usage')) {
|
|
250
|
+
const run = await card.agents.usage(agentId, startedAt) // until = now
|
|
251
|
+
run.requests // model requests (API round-trips)
|
|
252
|
+
run.inputTokens, run.outputTokens, run.cacheReadTokens, run.cacheWriteTokens, run.totalTokens
|
|
253
|
+
run.prompts, run.workingMs, run.elapsedMs, run.costMicroUsd
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
A `null` token field means **not reported** by that harness or provider (no
|
|
258
|
+
such field in its log, a local server that sends no cache counts, the OpenAI
|
|
259
|
+
APIs' missing cache writes) — show it as such, never as 0. The official
|
|
260
|
+
[Run Stats](https://github.com/glmn-ai/neurosquad-cards/tree/main/official-cards/run-stats)
|
|
261
|
+
card is built on it.
|
|
262
|
+
|
|
263
|
+
### Requests on a timeline: `card.agents.timeline`
|
|
264
|
+
|
|
265
|
+
Since SDK 1.2 (NeuroSquad 0.1.264): the same arrow-connected agent's model
|
|
266
|
+
requests one by one — completion time, send time where the harness's log has
|
|
267
|
+
one (`requestTimes: 'start-end'`, otherwise `'end'`), tokens, tool calls — plus
|
|
268
|
+
its status as consecutive spans and its turns (prompt → finish). Needs
|
|
269
|
+
`usage.read`. The official [Agent Pulse](https://github.com/glmn-ai/neurosquad-cards/tree/main/official-cards/agent-pulse)
|
|
270
|
+
card draws it as swimlanes.
|
|
271
|
+
|
|
241
272
|
### Network
|
|
242
273
|
|
|
243
274
|
```ts
|
|
@@ -366,7 +397,7 @@ permissions, visibility, optionally rate limits) and records everything:
|
|
|
366
397
|
`calls`, `storage`, `deliveries`, `prompts`, `logs`, `toasts`, `chrome`
|
|
367
398
|
(title/status/badge/overview/menu). Drive the card with `setVisibility`,
|
|
368
399
|
`setLanguage`, `setSettings`, `setSecret`, `sendPortMessage`, `requestPort`,
|
|
369
|
-
`callTool`, `setAgentStatus`, `agentOutput`, `suspend`, `touchFile`. The same
|
|
400
|
+
`callTool`, `setAgentStatus`, `setAgentUsage`, `setAgentTimeline`, `agentOutput`, `suspend`, `touchFile`. The same
|
|
370
401
|
mock powers the templates' browser preview when the page is opened outside the
|
|
371
402
|
app.
|
|
372
403
|
|
|
@@ -378,7 +409,7 @@ that the value fits the input's schema; `permissions.request` fails with
|
|
|
378
409
|
`NOT_VISIBLE` off screen; `fs.write` refuses the protected paths above. Options:
|
|
379
410
|
`settings` (initial values), `secrets` (`{ key: value }` for `secret`
|
|
380
411
|
settings — `{{secret:key}}` headers are filled, a missing one is
|
|
381
|
-
`INVALID_PARAMS`), `enforceRateLimits` (per-method throttles; off by default so
|
|
412
|
+
`INVALID_PARAMS`), `agentUsage` / `agentTimeline` (what `agents.usage` / `agents.timeline` return per agent, or a function of the call), `enforceRateLimits` (per-method throttles; off by default so
|
|
382
413
|
tests stay deterministic).
|
|
383
414
|
|
|
384
415
|
**Test in the app too.** The mock runs in your page, not in the sandboxed
|
|
@@ -426,6 +457,10 @@ New methods and events arrive within a protocol version; check with
|
|
|
426
457
|
`card.host.supports('method.name')`. A card built for a newer protocol is
|
|
427
458
|
refused by an older app with `PROTOCOL_MISMATCH`.
|
|
428
459
|
|
|
460
|
+
What changed in each version — new methods, the NeuroSquad version they need,
|
|
461
|
+
permissions, migration notes — is in [CHANGELOG.md](./CHANGELOG.md) (also on
|
|
462
|
+
the docs site: https://docs.neurosquad.ai/en/card-sdk/changelog).
|
|
463
|
+
|
|
429
464
|
## License
|
|
430
465
|
|
|
431
466
|
MIT
|
package/dist/card-sdk.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/*! @neurosquad/card-sdk 1.
|
|
1
|
+
/*! @neurosquad/card-sdk 1.2.0 | MIT */
|
|
2
2
|
|
|
3
3
|
// src/contract/version.ts
|
|
4
4
|
var CARD_PROTOCOL_VERSION = 1;
|
|
5
|
-
var CARD_SDK_CONTRACT_VERSION = "1.
|
|
5
|
+
var CARD_SDK_CONTRACT_VERSION = "1.2.0";
|
|
6
6
|
var MANIFEST_FILE = "neurosquad-card.json";
|
|
7
7
|
var MANIFEST_VERSION = 1;
|
|
8
8
|
var CARD_SCHEME = "nscard";
|
|
@@ -1756,7 +1756,10 @@ var PROTECTED_DIRS = /* @__PURE__ */ new Set([
|
|
|
1756
1756
|
".kilocode",
|
|
1757
1757
|
".windsurf",
|
|
1758
1758
|
".continue",
|
|
1759
|
-
".devcontainer"
|
|
1759
|
+
".devcontainer",
|
|
1760
|
+
// CI that runs whatever the config says on the next push.
|
|
1761
|
+
".circleci",
|
|
1762
|
+
".buildkite"
|
|
1760
1763
|
]);
|
|
1761
1764
|
var PROTECTED_FILES = /* @__PURE__ */ new Set([
|
|
1762
1765
|
".git",
|
|
@@ -1776,7 +1779,15 @@ var PROTECTED_FILES = /* @__PURE__ */ new Set([
|
|
|
1776
1779
|
".npmrc",
|
|
1777
1780
|
".yarnrc",
|
|
1778
1781
|
".yarnrc.yml",
|
|
1779
|
-
".pnpmfile.cjs"
|
|
1782
|
+
".pnpmfile.cjs",
|
|
1783
|
+
".gitlab-ci.yml",
|
|
1784
|
+
"azure-pipelines.yml",
|
|
1785
|
+
"jenkinsfile",
|
|
1786
|
+
".travis.yml",
|
|
1787
|
+
"bitbucket-pipelines.yml",
|
|
1788
|
+
".drone.yml",
|
|
1789
|
+
"appveyor.yml",
|
|
1790
|
+
".appveyor.yml"
|
|
1780
1791
|
]);
|
|
1781
1792
|
function protectedWriteReason(normalized) {
|
|
1782
1793
|
if (normalized === "") return null;
|
|
@@ -2036,11 +2047,19 @@ var RESERVED_NAMES = /* @__PURE__ */ new Set([
|
|
|
2036
2047
|
"squad",
|
|
2037
2048
|
"mcp",
|
|
2038
2049
|
"skill",
|
|
2050
|
+
"rtk",
|
|
2051
|
+
"caveman",
|
|
2052
|
+
"mem0",
|
|
2053
|
+
"codegraph",
|
|
2054
|
+
"graphify",
|
|
2055
|
+
"context7",
|
|
2056
|
+
"memory",
|
|
2039
2057
|
"official"
|
|
2040
2058
|
]);
|
|
2041
2059
|
var BUILTIN_MCP_TOOL_NAMES = [
|
|
2042
2060
|
"agent_read",
|
|
2043
2061
|
"agent_send",
|
|
2062
|
+
"agent_set_model",
|
|
2044
2063
|
"agent_wait",
|
|
2045
2064
|
"browser_click",
|
|
2046
2065
|
"browser_history",
|
|
@@ -2049,14 +2068,47 @@ var BUILTIN_MCP_TOOL_NAMES = [
|
|
|
2049
2068
|
"browser_screenshot",
|
|
2050
2069
|
"browser_snapshot",
|
|
2051
2070
|
"browser_type",
|
|
2071
|
+
"canvas_connect",
|
|
2072
|
+
"canvas_disconnect",
|
|
2073
|
+
"canvas_list",
|
|
2052
2074
|
"canvas_remove_card",
|
|
2053
2075
|
"canvas_spawn_card",
|
|
2076
|
+
"codegraph_check_index_coverage",
|
|
2077
|
+
"codegraph_detect_changes",
|
|
2078
|
+
"codegraph_get_architecture",
|
|
2079
|
+
"codegraph_get_code_snippet",
|
|
2080
|
+
"codegraph_get_file_outline",
|
|
2081
|
+
"codegraph_get_graph_schema",
|
|
2082
|
+
"codegraph_index_status",
|
|
2083
|
+
"codegraph_query_graph",
|
|
2084
|
+
"codegraph_reindex",
|
|
2085
|
+
"codegraph_search_code",
|
|
2086
|
+
"codegraph_search_graph",
|
|
2087
|
+
"codegraph_trace_path",
|
|
2088
|
+
"context7_query_docs",
|
|
2089
|
+
"context7_resolve_library_id",
|
|
2090
|
+
"graphify_affected",
|
|
2091
|
+
"graphify_community",
|
|
2092
|
+
"graphify_god_nodes",
|
|
2093
|
+
"graphify_neighbors",
|
|
2094
|
+
"graphify_node",
|
|
2095
|
+
"graphify_path",
|
|
2096
|
+
"graphify_query",
|
|
2097
|
+
"graphify_reindex",
|
|
2098
|
+
"graphify_stats",
|
|
2054
2099
|
"kanban_add",
|
|
2055
2100
|
"kanban_move",
|
|
2056
2101
|
"kanban_read",
|
|
2057
2102
|
"kanban_remove",
|
|
2058
2103
|
"kanban_write",
|
|
2104
|
+
"memory_add",
|
|
2105
|
+
"memory_delete",
|
|
2106
|
+
"memory_get",
|
|
2107
|
+
"memory_list",
|
|
2108
|
+
"memory_search",
|
|
2109
|
+
"memory_update",
|
|
2059
2110
|
"neurosquad_connections",
|
|
2111
|
+
"neurosquad_models",
|
|
2060
2112
|
"note_append",
|
|
2061
2113
|
"note_read",
|
|
2062
2114
|
"note_write",
|
|
@@ -2593,6 +2645,13 @@ var CARD_VISIBILITIES = [
|
|
|
2593
2645
|
"overview",
|
|
2594
2646
|
"hidden"
|
|
2595
2647
|
];
|
|
2648
|
+
var CARD_AGENT_STATUSES = [
|
|
2649
|
+
"working",
|
|
2650
|
+
"needs-input",
|
|
2651
|
+
"finished",
|
|
2652
|
+
"idle",
|
|
2653
|
+
"exited"
|
|
2654
|
+
];
|
|
2596
2655
|
var EVENT_TOPICS = [
|
|
2597
2656
|
"agents.status",
|
|
2598
2657
|
"agents.turn",
|
|
@@ -3011,6 +3070,32 @@ var METHOD_SPECS = {
|
|
|
3011
3070
|
permission: "usage.read",
|
|
3012
3071
|
params: obj({ period: { enum: ["today", "7d", "30d"] } }, ["period"]),
|
|
3013
3072
|
perMinute: 6
|
|
3073
|
+
},
|
|
3074
|
+
// Contract 1.1. Only agents connected to the card by an arrow (NOT_CONNECTED otherwise).
|
|
3075
|
+
"agents.usage": {
|
|
3076
|
+
permission: "usage.read",
|
|
3077
|
+
params: obj(
|
|
3078
|
+
{
|
|
3079
|
+
agentId: ID,
|
|
3080
|
+
since: { type: "integer", minimum: 0, maximum: 864e10 },
|
|
3081
|
+
until: { type: "integer", minimum: 0, maximum: 864e10 }
|
|
3082
|
+
},
|
|
3083
|
+
["agentId", "since"]
|
|
3084
|
+
),
|
|
3085
|
+
perMinute: 60
|
|
3086
|
+
},
|
|
3087
|
+
// Contract 1.2. Same scope as agents.usage: an arrow-connected AI agent.
|
|
3088
|
+
"agents.timeline": {
|
|
3089
|
+
permission: "usage.read",
|
|
3090
|
+
params: obj(
|
|
3091
|
+
{
|
|
3092
|
+
agentId: ID,
|
|
3093
|
+
since: { type: "integer", minimum: 0, maximum: 864e10 },
|
|
3094
|
+
until: { type: "integer", minimum: 0, maximum: 864e10 }
|
|
3095
|
+
},
|
|
3096
|
+
["agentId", "since"]
|
|
3097
|
+
),
|
|
3098
|
+
perMinute: 60
|
|
3014
3099
|
}
|
|
3015
3100
|
};
|
|
3016
3101
|
var CARD_METHOD_NAMES = Object.keys(METHOD_SPECS);
|
|
@@ -3030,7 +3115,7 @@ function permissionStates(declared, granted, resolveReason) {
|
|
|
3030
3115
|
}
|
|
3031
3116
|
|
|
3032
3117
|
// src/version.ts
|
|
3033
|
-
var SDK_VERSION = "1.
|
|
3118
|
+
var SDK_VERSION = "1.2.0";
|
|
3034
3119
|
|
|
3035
3120
|
// src/client/base64.ts
|
|
3036
3121
|
function bytesToBase64(bytes) {
|
|
@@ -4434,6 +4519,36 @@ var CardAgents = class {
|
|
|
4434
4519
|
lastReply(agentId) {
|
|
4435
4520
|
return this.card.call("agents.lastReply", { agentId });
|
|
4436
4521
|
}
|
|
4522
|
+
/**
|
|
4523
|
+
* One connected AI agent's run since `since` (epoch ms) — `usage.read`,
|
|
4524
|
+
* contract 1.1: check `card.host.supports('agents.usage')` first. Model
|
|
4525
|
+
* requests and tokens come from the harness's own log (the app's Usage
|
|
4526
|
+
* data), prompts and working time from the app's status history, so the
|
|
4527
|
+
* numbers are right even for time the card was off screen. A `null`
|
|
4528
|
+
* token field is not reported by that harness or provider — show it as
|
|
4529
|
+
* such, never as 0.
|
|
4530
|
+
*/
|
|
4531
|
+
usage(agentId, since, until) {
|
|
4532
|
+
return this.card.call("agents.usage", {
|
|
4533
|
+
agentId,
|
|
4534
|
+
since,
|
|
4535
|
+
...until !== void 0 ? { until } : {}
|
|
4536
|
+
});
|
|
4537
|
+
}
|
|
4538
|
+
/**
|
|
4539
|
+
* One connected AI agent's model requests and statuses since `since` —
|
|
4540
|
+
* `usage.read`, contract 1.2: check `card.host.supports('agents.timeline')`.
|
|
4541
|
+
* Each request has its completion time, its send time where the harness's
|
|
4542
|
+
* log records one (`requestTimes: 'start-end'`), tokens and tool calls;
|
|
4543
|
+
* statuses are consecutive spans and turns carry prompt / finish times.
|
|
4544
|
+
*/
|
|
4545
|
+
timeline(agentId, since, until) {
|
|
4546
|
+
return this.card.call("agents.timeline", {
|
|
4547
|
+
agentId,
|
|
4548
|
+
since,
|
|
4549
|
+
...until !== void 0 ? { until } : {}
|
|
4550
|
+
});
|
|
4551
|
+
}
|
|
4437
4552
|
/**
|
|
4438
4553
|
* Sends a prompt to a connected AI agent (`agents.prompt`, 6 per minute).
|
|
4439
4554
|
* While the agent works it is queued (`whenBusy: 'queue'`, default),
|
|
@@ -5131,6 +5246,7 @@ export {
|
|
|
5131
5246
|
BUILTIN_CARD_PORTS,
|
|
5132
5247
|
BUILTIN_MCP_TOOL_NAMES,
|
|
5133
5248
|
BUILTIN_PEER_KINDS,
|
|
5249
|
+
CARD_AGENT_STATUSES,
|
|
5134
5250
|
CARD_ERROR_CODES,
|
|
5135
5251
|
CARD_EVENT_NAMES,
|
|
5136
5252
|
CARD_HOST_ID_RE,
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/*! @neurosquad/card-sdk 1.
|
|
2
|
+
/*! @neurosquad/card-sdk 1.2.0 | MIT */
|
|
3
3
|
|
|
4
4
|
// src/version.ts
|
|
5
|
-
var SDK_VERSION = "1.
|
|
5
|
+
var SDK_VERSION = "1.2.0";
|
|
6
6
|
|
|
7
7
|
// src/cli/create.ts
|
|
8
8
|
import {
|
|
@@ -1421,11 +1421,19 @@ var RESERVED_NAMES = /* @__PURE__ */ new Set([
|
|
|
1421
1421
|
"squad",
|
|
1422
1422
|
"mcp",
|
|
1423
1423
|
"skill",
|
|
1424
|
+
"rtk",
|
|
1425
|
+
"caveman",
|
|
1426
|
+
"mem0",
|
|
1427
|
+
"codegraph",
|
|
1428
|
+
"graphify",
|
|
1429
|
+
"context7",
|
|
1430
|
+
"memory",
|
|
1424
1431
|
"official"
|
|
1425
1432
|
]);
|
|
1426
1433
|
var BUILTIN_MCP_TOOL_NAMES = [
|
|
1427
1434
|
"agent_read",
|
|
1428
1435
|
"agent_send",
|
|
1436
|
+
"agent_set_model",
|
|
1429
1437
|
"agent_wait",
|
|
1430
1438
|
"browser_click",
|
|
1431
1439
|
"browser_history",
|
|
@@ -1434,14 +1442,47 @@ var BUILTIN_MCP_TOOL_NAMES = [
|
|
|
1434
1442
|
"browser_screenshot",
|
|
1435
1443
|
"browser_snapshot",
|
|
1436
1444
|
"browser_type",
|
|
1445
|
+
"canvas_connect",
|
|
1446
|
+
"canvas_disconnect",
|
|
1447
|
+
"canvas_list",
|
|
1437
1448
|
"canvas_remove_card",
|
|
1438
1449
|
"canvas_spawn_card",
|
|
1450
|
+
"codegraph_check_index_coverage",
|
|
1451
|
+
"codegraph_detect_changes",
|
|
1452
|
+
"codegraph_get_architecture",
|
|
1453
|
+
"codegraph_get_code_snippet",
|
|
1454
|
+
"codegraph_get_file_outline",
|
|
1455
|
+
"codegraph_get_graph_schema",
|
|
1456
|
+
"codegraph_index_status",
|
|
1457
|
+
"codegraph_query_graph",
|
|
1458
|
+
"codegraph_reindex",
|
|
1459
|
+
"codegraph_search_code",
|
|
1460
|
+
"codegraph_search_graph",
|
|
1461
|
+
"codegraph_trace_path",
|
|
1462
|
+
"context7_query_docs",
|
|
1463
|
+
"context7_resolve_library_id",
|
|
1464
|
+
"graphify_affected",
|
|
1465
|
+
"graphify_community",
|
|
1466
|
+
"graphify_god_nodes",
|
|
1467
|
+
"graphify_neighbors",
|
|
1468
|
+
"graphify_node",
|
|
1469
|
+
"graphify_path",
|
|
1470
|
+
"graphify_query",
|
|
1471
|
+
"graphify_reindex",
|
|
1472
|
+
"graphify_stats",
|
|
1439
1473
|
"kanban_add",
|
|
1440
1474
|
"kanban_move",
|
|
1441
1475
|
"kanban_read",
|
|
1442
1476
|
"kanban_remove",
|
|
1443
1477
|
"kanban_write",
|
|
1478
|
+
"memory_add",
|
|
1479
|
+
"memory_delete",
|
|
1480
|
+
"memory_get",
|
|
1481
|
+
"memory_list",
|
|
1482
|
+
"memory_search",
|
|
1483
|
+
"memory_update",
|
|
1444
1484
|
"neurosquad_connections",
|
|
1485
|
+
"neurosquad_models",
|
|
1445
1486
|
"note_append",
|
|
1446
1487
|
"note_read",
|
|
1447
1488
|
"note_write",
|
package/dist/react.js
CHANGED
package/dist/testing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @neurosquad/card-sdk 1.
|
|
1
|
+
/*! @neurosquad/card-sdk 1.2.0 | MIT */
|
|
2
2
|
|
|
3
3
|
// src/testing/mockHost.ts
|
|
4
4
|
import {
|
|
@@ -422,6 +422,18 @@ var MockHost = class {
|
|
|
422
422
|
this.emit("tools.cancel", { callId });
|
|
423
423
|
pending?.reject(new HostError("USER_CANCELLED", "cancelled"));
|
|
424
424
|
}
|
|
425
|
+
/** What `agents.timeline` returns for this agent from now on (merged over an empty timeline). */
|
|
426
|
+
setAgentTimeline(agentId, timeline) {
|
|
427
|
+
const current = this.options.agentTimeline;
|
|
428
|
+
const table = typeof current === "object" && current !== null ? current : {};
|
|
429
|
+
this.options.agentTimeline = { ...table, [agentId]: timeline };
|
|
430
|
+
}
|
|
431
|
+
/** What `agents.usage` returns for this agent from now on (merged over an all-zero run). */
|
|
432
|
+
setAgentUsage(agentId, usage) {
|
|
433
|
+
const current = this.options.agentUsage;
|
|
434
|
+
const table = typeof current === "object" && current !== null ? current : {};
|
|
435
|
+
this.options.agentUsage = { ...table, [agentId]: usage };
|
|
436
|
+
}
|
|
425
437
|
/**
|
|
426
438
|
* Changes an agent's status; the card gets `agents.status` if subscribed,
|
|
427
439
|
* and `agents.turn` by the app's rule: `start` on entering `working`,
|
|
@@ -854,6 +866,64 @@ var MockHost = class {
|
|
|
854
866
|
case "clipboard.writeText":
|
|
855
867
|
this.clipboard.push(str("text"));
|
|
856
868
|
return void 0;
|
|
869
|
+
case "agents.usage": {
|
|
870
|
+
const agent = this.connectedAgent(str("agentId"));
|
|
871
|
+
if (agent.kind !== "ai") fail("INVALID_PARAMS", `${agent.name} is not an AI agent`);
|
|
872
|
+
const since = p["since"];
|
|
873
|
+
const until = p["until"] ?? Date.now();
|
|
874
|
+
if (since > until) fail("INVALID_PARAMS", '"since" is after "until"');
|
|
875
|
+
const source = this.options.agentUsage;
|
|
876
|
+
const given = typeof source === "function" ? source(agent.id, since, until) : source?.[agent.id] ?? {};
|
|
877
|
+
const base = {
|
|
878
|
+
agentId: agent.id,
|
|
879
|
+
harness: agent.harness,
|
|
880
|
+
since,
|
|
881
|
+
until,
|
|
882
|
+
status: agent.status,
|
|
883
|
+
usageReadable: true,
|
|
884
|
+
requests: 0,
|
|
885
|
+
inputTokens: 0,
|
|
886
|
+
outputTokens: 0,
|
|
887
|
+
cacheReadTokens: 0,
|
|
888
|
+
cacheWriteTokens: 0,
|
|
889
|
+
reasoningTokens: 0,
|
|
890
|
+
totalTokens: 0,
|
|
891
|
+
costMicroUsd: null,
|
|
892
|
+
costPartial: false,
|
|
893
|
+
...agent.model ? { model: agent.model } : {},
|
|
894
|
+
prompts: 0,
|
|
895
|
+
workingMs: 0,
|
|
896
|
+
firstPromptAt: null,
|
|
897
|
+
lastFinishedAt: null,
|
|
898
|
+
elapsedMs: null,
|
|
899
|
+
timingComplete: true,
|
|
900
|
+
scannedAt: Date.now()
|
|
901
|
+
};
|
|
902
|
+
return { ...base, ...given };
|
|
903
|
+
}
|
|
904
|
+
case "agents.timeline": {
|
|
905
|
+
const agent = this.connectedAgent(str("agentId"));
|
|
906
|
+
if (agent.kind !== "ai") fail("INVALID_PARAMS", `${agent.name} is not an AI agent`);
|
|
907
|
+
const since = p["since"];
|
|
908
|
+
const until = p["until"] ?? Date.now();
|
|
909
|
+
if (since > until) fail("INVALID_PARAMS", '"since" is after "until"');
|
|
910
|
+
const source = this.options.agentTimeline;
|
|
911
|
+
const given = typeof source === "function" ? source(agent.id, since, until) : source?.[agent.id] ?? {};
|
|
912
|
+
const base = {
|
|
913
|
+
agentId: agent.id,
|
|
914
|
+
harness: agent.harness,
|
|
915
|
+
since,
|
|
916
|
+
until,
|
|
917
|
+
usageReadable: true,
|
|
918
|
+
requestTimes: "start-end",
|
|
919
|
+
requests: [],
|
|
920
|
+
truncated: false,
|
|
921
|
+
statuses: [{ status: agent.status, from: since, to: until }],
|
|
922
|
+
turns: [],
|
|
923
|
+
scannedAt: Date.now()
|
|
924
|
+
};
|
|
925
|
+
return { ...base, ...given };
|
|
926
|
+
}
|
|
857
927
|
case "usage.summary":
|
|
858
928
|
return this.options.usage ?? {
|
|
859
929
|
period: str("period"),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AgentInfo, type CardEventName, type CardEvents, type CardInstanceInfo, type CardMethodName, type CardSize, type CardTone, type CardVisibility, type EventTopic, type FsEntry, type FsStat, type HostContext, type HostIconName, type I18nSnapshot, type JsonValue, type MenuItem, type MethodParams, type MethodResult, type OverviewContent, type PeerInfo, type PermissionId, type PermissionState, type PortInfo, type SettingsSnapshot, type SettingValue, type ThemeSnapshot, type ToolResultPayload, type UsageSummary, type WorkspaceInfo } from '../contract/index.js';
|
|
1
|
+
import { type AgentInfo, type AgentTimeline, type AgentUsage, type CardEventName, type CardEvents, type CardInstanceInfo, type CardMethodName, type CardSize, type CardTone, type CardVisibility, type EventTopic, type FsEntry, type FsStat, type HostContext, type HostIconName, type I18nSnapshot, type JsonValue, type MenuItem, type MethodParams, type MethodResult, type OverviewContent, type PeerInfo, type PermissionId, type PermissionState, type PortInfo, type SettingsSnapshot, type SettingValue, type ThemeSnapshot, type ToolResultPayload, type UsageSummary, type WorkspaceInfo } from '../contract/index.js';
|
|
2
2
|
import type { CallOptions, Channel } from './channel.js';
|
|
3
3
|
import { CardResponse, ChunkQueue, type CardFetchInit } from './net.js';
|
|
4
4
|
import { type ThemeTarget } from './theme.js';
|
|
@@ -415,6 +415,24 @@ export declare class CardAgents {
|
|
|
415
415
|
text: string | null;
|
|
416
416
|
at: number | null;
|
|
417
417
|
}>;
|
|
418
|
+
/**
|
|
419
|
+
* One connected AI agent's run since `since` (epoch ms) — `usage.read`,
|
|
420
|
+
* contract 1.1: check `card.host.supports('agents.usage')` first. Model
|
|
421
|
+
* requests and tokens come from the harness's own log (the app's Usage
|
|
422
|
+
* data), prompts and working time from the app's status history, so the
|
|
423
|
+
* numbers are right even for time the card was off screen. A `null`
|
|
424
|
+
* token field is not reported by that harness or provider — show it as
|
|
425
|
+
* such, never as 0.
|
|
426
|
+
*/
|
|
427
|
+
usage(agentId: string, since: number, until?: number): Promise<AgentUsage>;
|
|
428
|
+
/**
|
|
429
|
+
* One connected AI agent's model requests and statuses since `since` —
|
|
430
|
+
* `usage.read`, contract 1.2: check `card.host.supports('agents.timeline')`.
|
|
431
|
+
* Each request has its completion time, its send time where the harness's
|
|
432
|
+
* log records one (`requestTimes: 'start-end'`), tokens and tool calls;
|
|
433
|
+
* statuses are consecutive spans and turns carry prompt / finish times.
|
|
434
|
+
*/
|
|
435
|
+
timeline(agentId: string, since: number, until?: number): Promise<AgentTimeline>;
|
|
418
436
|
/**
|
|
419
437
|
* Sends a prompt to a connected AI agent (`agents.prompt`, 6 per minute).
|
|
420
438
|
* While the agent works it is queued (`whenBusy: 'queue'`, default),
|
|
@@ -17,6 +17,8 @@ export type CardVisibility =
|
|
|
17
17
|
| 'hidden';
|
|
18
18
|
export declare const CARD_VISIBILITIES: readonly CardVisibility[];
|
|
19
19
|
export type AgentStatus = 'working' | 'needs-input' | 'finished' | 'idle' | 'exited';
|
|
20
|
+
/** Every AgentStatus. Append only: main persists statuses by their index here. */
|
|
21
|
+
export declare const CARD_AGENT_STATUSES: readonly AgentStatus[];
|
|
20
22
|
export interface CardInstanceInfo {
|
|
21
23
|
/** The card's id on the canvas (the Agent record id). */
|
|
22
24
|
instanceId: string;
|
|
@@ -216,6 +218,119 @@ export interface UsageSummary {
|
|
|
216
218
|
/** Some rows had no price and are not in the total. */
|
|
217
219
|
partial: boolean;
|
|
218
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* One agent's run inside a time window — `agents.usage` (spec §8.6.1). The
|
|
223
|
+
* token numbers are the model requests the harness itself logged (the Usage
|
|
224
|
+
* section's data), the timing comes from the app's status history.
|
|
225
|
+
*
|
|
226
|
+
* A token field is `null` when it is **not reported**: the harness's log has
|
|
227
|
+
* no such field, or the provider sends none (a local or custom server that
|
|
228
|
+
* reports no cache, the OpenAI APIs that have no cache writes) — never a 0
|
|
229
|
+
* that would read as a measurement. Every number is an integer.
|
|
230
|
+
*/
|
|
231
|
+
export interface AgentUsage {
|
|
232
|
+
agentId: string;
|
|
233
|
+
harness: string;
|
|
234
|
+
/** The window: epoch ms, `since` inclusive. `until` is now unless the call gave one. */
|
|
235
|
+
since: number;
|
|
236
|
+
until: number;
|
|
237
|
+
/** The agent's status at `until`, as far as the app's history knows. */
|
|
238
|
+
status: AgentStatus;
|
|
239
|
+
/** False when the app reads no usage log for this harness (Amp, Cursor): every token field is null. */
|
|
240
|
+
usageReadable: boolean;
|
|
241
|
+
/**
|
|
242
|
+
* Model requests (API round-trips) the harness logged in the window. Null
|
|
243
|
+
* when its log keeps running totals rather than requests (Hermes, Factory
|
|
244
|
+
* Droid) or when usage is not readable.
|
|
245
|
+
*/
|
|
246
|
+
requests: number | null;
|
|
247
|
+
/** Uncached input (prompt) tokens. */
|
|
248
|
+
inputTokens: number | null;
|
|
249
|
+
/** Output tokens, reasoning included. */
|
|
250
|
+
outputTokens: number | null;
|
|
251
|
+
cacheReadTokens: number | null;
|
|
252
|
+
cacheWriteTokens: number | null;
|
|
253
|
+
/** The reasoning part of `outputTokens`. */
|
|
254
|
+
reasoningTokens: number | null;
|
|
255
|
+
/** input + output + cache read + cache write (the reported ones). */
|
|
256
|
+
totalTokens: number | null;
|
|
257
|
+
/** Micro-dollars, rounded up; null when no request in the window has a known price. */
|
|
258
|
+
costMicroUsd: number | null;
|
|
259
|
+
/** Some requests had no known price and are not in `costMicroUsd`. */
|
|
260
|
+
costPartial: boolean;
|
|
261
|
+
/** The model of the newest request in the window (else the agent's configured one). */
|
|
262
|
+
model?: string;
|
|
263
|
+
/** Who served it: `anthropic`, `openai`, `openrouter`, a custom provider's name… */
|
|
264
|
+
provider?: string;
|
|
265
|
+
/** Turns the agent was given (a permission answer continues the same turn). */
|
|
266
|
+
prompts: number;
|
|
267
|
+
/** Time spent working inside the window (waiting on the user excluded). */
|
|
268
|
+
workingMs: number;
|
|
269
|
+
firstPromptAt: number | null;
|
|
270
|
+
lastFinishedAt: number | null;
|
|
271
|
+
/** First prompt → latest finish (→ `until` while a turn is under way); null before the first prompt. */
|
|
272
|
+
elapsedMs: number | null;
|
|
273
|
+
/**
|
|
274
|
+
* False when the app's status history starts after `since` (the agent or
|
|
275
|
+
* the app was started later, or old history was trimmed): the timing counts
|
|
276
|
+
* only what the app saw.
|
|
277
|
+
*/
|
|
278
|
+
timingComplete: boolean;
|
|
279
|
+
/** When the usage logs were last read (epoch ms). */
|
|
280
|
+
scannedAt: number;
|
|
281
|
+
}
|
|
282
|
+
/** One model request on an agent's timeline (`agents.timeline`). */
|
|
283
|
+
export interface AgentRequestSpan {
|
|
284
|
+
/** When the response completed (epoch ms) — always known. */
|
|
285
|
+
endedAt: number;
|
|
286
|
+
/** When the request was sent; null when the harness's log does not say (`AgentTimeline.requestTimes`). */
|
|
287
|
+
startedAt: number | null;
|
|
288
|
+
model?: string;
|
|
289
|
+
/** Same "not reported" rules as AgentUsage, per request. */
|
|
290
|
+
inputTokens: number | null;
|
|
291
|
+
outputTokens: number | null;
|
|
292
|
+
cacheReadTokens: number | null;
|
|
293
|
+
cacheWriteTokens: number | null;
|
|
294
|
+
/** Tool calls the model asked for in this response, in order (where the log names them). */
|
|
295
|
+
tools?: string[];
|
|
296
|
+
}
|
|
297
|
+
/** A turn (a prompt and what the agent did with it) on the timeline. */
|
|
298
|
+
export interface AgentTurnSpan {
|
|
299
|
+
start: number;
|
|
300
|
+
/** Null while under way. */
|
|
301
|
+
end: number | null;
|
|
302
|
+
endedAs?: AgentStatus;
|
|
303
|
+
/** False for a turn that ended without one model request (a status blip; not a prompt). */
|
|
304
|
+
counted: boolean;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* One connected agent's requests and statuses inside a window —
|
|
308
|
+
* `agents.timeline` (spec §8.6.2): every model request with its start, end,
|
|
309
|
+
* tokens and tool calls (from the harness's own log), the agent's status as
|
|
310
|
+
* consecutive spans and its turns (from the app's status history).
|
|
311
|
+
*/
|
|
312
|
+
export interface AgentTimeline {
|
|
313
|
+
agentId: string;
|
|
314
|
+
harness: string;
|
|
315
|
+
since: number;
|
|
316
|
+
until: number;
|
|
317
|
+
usageReadable: boolean;
|
|
318
|
+
/**
|
|
319
|
+
* `start-end`: requests carry their send time (bars); `end`: completion
|
|
320
|
+
* times only (ticks); `none`: no readable request log.
|
|
321
|
+
*/
|
|
322
|
+
requestTimes: 'start-end' | 'end' | 'none';
|
|
323
|
+
/** Oldest first; at most 2000 (the newest are kept and `truncated` is set). */
|
|
324
|
+
requests: AgentRequestSpan[];
|
|
325
|
+
truncated: boolean;
|
|
326
|
+
statuses: {
|
|
327
|
+
status: AgentStatus;
|
|
328
|
+
from: number;
|
|
329
|
+
to: number;
|
|
330
|
+
}[];
|
|
331
|
+
turns: AgentTurnSpan[];
|
|
332
|
+
scannedAt: number;
|
|
333
|
+
}
|
|
219
334
|
export type EventTopic = 'agents.status' | 'agents.turn' | 'agents.changed' | 'agents.output' | 'storage.changed';
|
|
220
335
|
export declare const EVENT_TOPICS: readonly EventTopic[];
|
|
221
336
|
/** Permission each subscribable topic needs. */
|
|
@@ -686,6 +801,24 @@ export interface CardMethods {
|
|
|
686
801
|
};
|
|
687
802
|
result: UsageSummary;
|
|
688
803
|
};
|
|
804
|
+
/** Added within protocol 1 (contract 1.1): check `card.host.supports('agents.usage')`. */
|
|
805
|
+
'agents.usage': {
|
|
806
|
+
params: {
|
|
807
|
+
agentId: string;
|
|
808
|
+
since: number;
|
|
809
|
+
until?: number;
|
|
810
|
+
};
|
|
811
|
+
result: AgentUsage;
|
|
812
|
+
};
|
|
813
|
+
/** Added within protocol 1 (contract 1.2): check `card.host.supports('agents.timeline')`. */
|
|
814
|
+
'agents.timeline': {
|
|
815
|
+
params: {
|
|
816
|
+
agentId: string;
|
|
817
|
+
since: number;
|
|
818
|
+
until?: number;
|
|
819
|
+
};
|
|
820
|
+
result: AgentTimeline;
|
|
821
|
+
};
|
|
689
822
|
}
|
|
690
823
|
export type CardMethodName = keyof CardMethods;
|
|
691
824
|
export type MethodParams<M extends CardMethodName> = CardMethods[M]['params'];
|
|
@@ -96,7 +96,8 @@ export declare function isGitInternalPath(normalized: string): boolean;
|
|
|
96
96
|
* agents or developer tools run commands without a build step (MC-10): agent
|
|
97
97
|
* hooks and MCP configs (`.claude/`, `.mcp.json`, …), agent instructions
|
|
98
98
|
* (`CLAUDE.md`, `AGENTS.md`, …: persistent prompt injection), editor tasks
|
|
99
|
-
* (`.vscode/`), git hooks managers (`.husky/`), CI (`.github/workflows
|
|
99
|
+
* (`.vscode/`), git hooks managers (`.husky/`), CI (`.github/workflows/`,
|
|
100
|
+
* `.gitlab-ci.yml`, `Jenkinsfile`, `.circleci/`, …),
|
|
100
101
|
* direnv and package-manager rc files. Case-insensitive.
|
|
101
102
|
*/
|
|
102
103
|
export declare function protectedWriteReason(normalized: string): string | null;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { CardLanguage } from './localized.js';
|
|
2
|
+
import { type PermissionId } from './permissions.js';
|
|
3
|
+
export declare const VERIFIED_SCHEMA_VERSION = 1;
|
|
4
|
+
/** Where the catalog lives (a public repository of the official organization). */
|
|
5
|
+
export declare const VERIFIED_REPO: {
|
|
6
|
+
owner: string;
|
|
7
|
+
repo: string;
|
|
8
|
+
file: string;
|
|
9
|
+
};
|
|
10
|
+
export declare const VERIFIED_CATEGORIES: readonly ["agents", "productivity", "dev-tools", "data", "integrations", "fun", "other"];
|
|
11
|
+
export type VerifiedCategory = (typeof VERIFIED_CATEGORIES)[number];
|
|
12
|
+
export declare const VERIFIED_LIMITS: {
|
|
13
|
+
readonly fileBytes: number;
|
|
14
|
+
readonly entries: 5000;
|
|
15
|
+
readonly id: 64;
|
|
16
|
+
readonly name: 80;
|
|
17
|
+
readonly description: 600;
|
|
18
|
+
readonly notes: 1000;
|
|
19
|
+
readonly author: 100;
|
|
20
|
+
readonly reviewer: 100;
|
|
21
|
+
readonly license: 64;
|
|
22
|
+
readonly url: 500;
|
|
23
|
+
readonly version: 64;
|
|
24
|
+
readonly tag: 32;
|
|
25
|
+
readonly tags: 16;
|
|
26
|
+
readonly hosts: 32;
|
|
27
|
+
readonly screenshots: 8;
|
|
28
|
+
/** How many skipped entries are reported (the rest are only counted). */
|
|
29
|
+
readonly reportedSkips: 50;
|
|
30
|
+
};
|
|
31
|
+
/** Text in the app's three languages; `en` is required and the fallback. */
|
|
32
|
+
export type VerifiedText = {
|
|
33
|
+
en: string;
|
|
34
|
+
} & Partial<Record<Exclude<CardLanguage, 'en'>, string>>;
|
|
35
|
+
export interface VerifiedEntry {
|
|
36
|
+
/** Catalog slug, unique: `[a-z0-9-]{2,64}`. */
|
|
37
|
+
id: string;
|
|
38
|
+
/** The package's manifest `name` (checked against the downloaded manifest at install). */
|
|
39
|
+
packageId: string;
|
|
40
|
+
name: VerifiedText;
|
|
41
|
+
description: VerifiedText;
|
|
42
|
+
author: {
|
|
43
|
+
name: string;
|
|
44
|
+
github?: string;
|
|
45
|
+
};
|
|
46
|
+
/** `owner/repo`, lowercased (GitHub names are case-insensitive). */
|
|
47
|
+
repo: string;
|
|
48
|
+
/** Package folder inside the repository (POSIX, case kept); absent = the root. */
|
|
49
|
+
path?: string;
|
|
50
|
+
/** Manifest version that was reviewed. */
|
|
51
|
+
version: string;
|
|
52
|
+
/** The reviewed commit (40 hex). */
|
|
53
|
+
commit: string;
|
|
54
|
+
/** sha256 hex of the package folder at that commit (the installer's tree hash). */
|
|
55
|
+
treeHash: string;
|
|
56
|
+
permissions: PermissionId[];
|
|
57
|
+
optionalPermissions: PermissionId[];
|
|
58
|
+
networkHosts: string[];
|
|
59
|
+
tags: string[];
|
|
60
|
+
category: VerifiedCategory;
|
|
61
|
+
license: string;
|
|
62
|
+
homepage?: string;
|
|
63
|
+
/** Paths inside the package. */
|
|
64
|
+
icon?: string;
|
|
65
|
+
screenshots: string[];
|
|
66
|
+
minAppVersion?: string;
|
|
67
|
+
/** Informational: the entry says it is the team's own card (the Official badge is still earned from GitHub). */
|
|
68
|
+
official: boolean;
|
|
69
|
+
/** `YYYY-MM-DD` or an ISO date-time. */
|
|
70
|
+
verifiedAt: string;
|
|
71
|
+
reviewer: string;
|
|
72
|
+
notes?: VerifiedText;
|
|
73
|
+
}
|
|
74
|
+
export interface VerifiedCatalog {
|
|
75
|
+
schemaVersion: 1;
|
|
76
|
+
updatedAt?: string;
|
|
77
|
+
cards: VerifiedEntry[];
|
|
78
|
+
}
|
|
79
|
+
export interface VerifiedSkip {
|
|
80
|
+
index: number;
|
|
81
|
+
id?: string;
|
|
82
|
+
reason: string;
|
|
83
|
+
}
|
|
84
|
+
export type VerifiedParseResult = {
|
|
85
|
+
ok: true;
|
|
86
|
+
catalog: VerifiedCatalog;
|
|
87
|
+
skipped: VerifiedSkip[];
|
|
88
|
+
skippedCount: number;
|
|
89
|
+
} | {
|
|
90
|
+
ok: false;
|
|
91
|
+
code: 'too-large' | 'not-json' | 'bad-shape' | 'unsupported-version';
|
|
92
|
+
error: string;
|
|
93
|
+
};
|
|
94
|
+
/** Removes control, bidi and invisible characters and trims. */
|
|
95
|
+
export declare function cleanVerifiedText(text: string, multiline?: boolean): string;
|
|
96
|
+
/**
|
|
97
|
+
* Validates a parsed `verified.json`. Invalid entries, a second entry with
|
|
98
|
+
* the same id and a second entry for the same source (repo + path) are
|
|
99
|
+
* skipped; the rest is kept.
|
|
100
|
+
*/
|
|
101
|
+
export declare function validateVerifiedCatalog(raw: unknown): VerifiedParseResult;
|
|
102
|
+
/** Parses the file's text (size-checked) and validates it. */
|
|
103
|
+
export declare function parseVerifiedText(textValue: string): VerifiedParseResult;
|
|
104
|
+
/** `owner/repo[/path]` — owner and repo lowercased, the path as written. */
|
|
105
|
+
export declare function verifiedSourceKey(repo: string, path?: string): string;
|
|
106
|
+
/** The catalog entry for a GitHub package source, if any. */
|
|
107
|
+
export declare function findVerifiedEntry(entries: readonly VerifiedEntry[], source: {
|
|
108
|
+
owner: string;
|
|
109
|
+
repo: string;
|
|
110
|
+
subdir?: string;
|
|
111
|
+
}): VerifiedEntry | undefined;
|
|
112
|
+
/**
|
|
113
|
+
* How an installed package relates to its catalog entry:
|
|
114
|
+
* - `verified`: same commit and the installed files hash to the reviewed tree hash
|
|
115
|
+
* - `mismatch`: same commit, different files (never shown as verified)
|
|
116
|
+
* - `update`: the catalog points at a newer version
|
|
117
|
+
* - `other-version`: another commit that is not newer (e.g. a branch head)
|
|
118
|
+
*/
|
|
119
|
+
export type VerifiedStatus = 'verified' | 'mismatch' | 'update' | 'other-version';
|
|
120
|
+
export declare function verifiedStatusFor(entry: VerifiedEntry, installed: {
|
|
121
|
+
commit: string | null;
|
|
122
|
+
treeHash: string;
|
|
123
|
+
version: string;
|
|
124
|
+
}): VerifiedStatus;
|
|
125
|
+
/** What "Install" resolves: the entry's repo and folder pinned to the reviewed commit. */
|
|
126
|
+
export declare function verifiedInstallInput(entry: VerifiedEntry): string;
|
|
127
|
+
/** SemVer precedence of the release part (pre-release sorts before the release). */
|
|
128
|
+
export declare function compareSemver(a: string, b: string): number;
|
|
129
|
+
/**
|
|
130
|
+
* Local search: every word of the query must appear somewhere (names in all
|
|
131
|
+
* three languages, descriptions, tags, author); entries whose name matches
|
|
132
|
+
* come first, otherwise the catalog order is kept.
|
|
133
|
+
*/
|
|
134
|
+
export declare function searchVerified(entries: readonly VerifiedEntry[], query: string, category?: VerifiedCategory | null): VerifiedEntry[];
|
|
135
|
+
/** The entry's text in `language`, falling back to English. */
|
|
136
|
+
export declare function verifiedText(value: VerifiedText | undefined, language: CardLanguage): string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Major version of the wire protocol. A card built for a higher one is refused with PROTOCOL_MISMATCH. */
|
|
2
2
|
export declare const CARD_PROTOCOL_VERSION = 1;
|
|
3
3
|
/** Version of this contract (and of `@neurosquad/card-sdk`'s protocol layer). */
|
|
4
|
-
export declare const CARD_SDK_CONTRACT_VERSION = "1.
|
|
4
|
+
export declare const CARD_SDK_CONTRACT_VERSION = "1.2.0";
|
|
5
5
|
/** The manifest at the root of every card package. */
|
|
6
6
|
export declare const MANIFEST_FILE = "neurosquad-card.json";
|
|
7
7
|
/** `manifestVersion` this app reads. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AgentInfo, type CardEvents, type CardEventName, type CardLanguage, type CardManifest, type CardMethodName, type CardSize, type CardVisibility, type EventTopic, type HostContext, type JsonValue, type MethodParams, type MethodResult, type NormalizedManifest, type PeerInfo, type PermissionId, type PortInfo, type SettingValue, type ThemeSnapshot, type ToolResultPayload, type UsageSummary, type Card, type ConnectOptions } from '../index.js';
|
|
1
|
+
import { type AgentInfo, type AgentTimeline, type AgentUsage, type CardEvents, type CardEventName, type CardLanguage, type CardManifest, type CardMethodName, type CardSize, type CardVisibility, type EventTopic, type HostContext, type JsonValue, type MethodParams, type MethodResult, type NormalizedManifest, type PeerInfo, type PermissionId, type PortInfo, type SettingValue, type ThemeSnapshot, type ToolResultPayload, type UsageSummary, type Card, type ConnectOptions } from '../index.js';
|
|
2
2
|
/** A peer card in the mock: its description plus optional behaviour. */
|
|
3
3
|
export interface MockPeer extends PeerInfo {
|
|
4
4
|
/** Retained output values (`ports.read`). */
|
|
@@ -66,6 +66,13 @@ export interface MockHostOptions {
|
|
|
66
66
|
enforceRateLimits?: boolean;
|
|
67
67
|
/** Usage summary for `usage.summary`. */
|
|
68
68
|
usage?: UsageSummary;
|
|
69
|
+
/**
|
|
70
|
+
* What `agents.usage` returns per agent id (merged over an all-zero run of
|
|
71
|
+
* that agent), or a function of the call. Change it later with `setAgentUsage`.
|
|
72
|
+
*/
|
|
73
|
+
/** What `agents.timeline` returns per agent id (merged over an empty timeline), or a function of the call. */
|
|
74
|
+
agentTimeline?: Record<string, Partial<AgentTimeline>> | ((agentId: string, since: number, until: number) => Partial<AgentTimeline>);
|
|
75
|
+
agentUsage?: Record<string, Partial<AgentUsage>> | ((agentId: string, since: number, until: number) => Partial<AgentUsage>);
|
|
69
76
|
/**
|
|
70
77
|
* Initial setting values (as if the user had saved them in the host form).
|
|
71
78
|
* Same as calling `setSettings(values)` before `connect()`.
|
|
@@ -268,6 +275,10 @@ export declare class MockHost {
|
|
|
268
275
|
};
|
|
269
276
|
/** Cancels a running tool call (`tools.cancel`); the promise from callTool rejects. */
|
|
270
277
|
cancelTool(callId: string): void;
|
|
278
|
+
/** What `agents.timeline` returns for this agent from now on (merged over an empty timeline). */
|
|
279
|
+
setAgentTimeline(agentId: string, timeline: Partial<AgentTimeline>): void;
|
|
280
|
+
/** What `agents.usage` returns for this agent from now on (merged over an all-zero run). */
|
|
281
|
+
setAgentUsage(agentId: string, usage: Partial<AgentUsage>): void;
|
|
271
282
|
/**
|
|
272
283
|
* Changes an agent's status; the card gets `agents.status` if subscribed,
|
|
273
284
|
* and `agents.turn` by the app's rule: `start` on entering `working`,
|
package/dist/types/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Version of `@neurosquad/card-sdk` (sent to the host in `host.ready`). Kept equal to package.json by a test. */
|
|
2
|
-
export declare const SDK_VERSION = "1.
|
|
2
|
+
export declare const SDK_VERSION = "1.2.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neurosquad/card-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Build custom cards for NeuroSquad: a typed client for the card API, React hooks, an optional dark UI kit and the neurosquad-card CLI (create, dev, validate, pack).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "NeuroSquad",
|
|
@@ -39,13 +39,14 @@
|
|
|
39
39
|
"./package.json": "./package.json"
|
|
40
40
|
},
|
|
41
41
|
"bin": {
|
|
42
|
-
"neurosquad-card": "
|
|
42
|
+
"neurosquad-card": "dist/cli.js"
|
|
43
43
|
},
|
|
44
44
|
"files": [
|
|
45
45
|
"dist",
|
|
46
46
|
"templates",
|
|
47
47
|
"schema",
|
|
48
48
|
"README.md",
|
|
49
|
+
"CHANGELOG.md",
|
|
49
50
|
"LICENSE"
|
|
50
51
|
],
|
|
51
52
|
"engines": {
|