@getbourdon/participants 0.1.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/LICENSE +92 -0
- package/LICENSE_FAQ.md +79 -0
- package/README.md +61 -0
- package/dist/base.d.ts +81 -0
- package/dist/base.js +52 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +33 -0
- package/dist/network-base.d.ts +108 -0
- package/dist/network-base.js +248 -0
- package/dist/readers/claude-code.d.ts +33 -0
- package/dist/readers/claude-code.js +552 -0
- package/dist/readers/github-copilot.d.ts +34 -0
- package/dist/readers/github-copilot.js +242 -0
- package/dist/readers/hermes.d.ts +37 -0
- package/dist/readers/hermes.js +426 -0
- package/dist/registry.d.ts +38 -0
- package/dist/registry.js +73 -0
- package/dist/sqlite-base.d.ts +65 -0
- package/dist/sqlite-base.js +163 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
License text copyright (c) 2020 MariaDB Corporation Ab, All Rights Reserved.
|
|
2
|
+
"Business Source License" is a trademark of MariaDB Corporation Ab.
|
|
3
|
+
|
|
4
|
+
Parameters
|
|
5
|
+
|
|
6
|
+
Licensor: RADLAB LLC
|
|
7
|
+
Licensed Work: Bourdon Version 0.2.0 or later. The Licensed Work is (c) 2026
|
|
8
|
+
RADLAB LLC.
|
|
9
|
+
Additional Use Grant: You may make production use of the Licensed Work, provided
|
|
10
|
+
Your use does not include offering the Licensed Work to third
|
|
11
|
+
parties on a hosted or embedded basis in order to compete with
|
|
12
|
+
RADLAB LLC's paid version(s) of the Licensed Work. For purposes
|
|
13
|
+
of this license:
|
|
14
|
+
|
|
15
|
+
A "competitive offering" is a Product that is offered to third
|
|
16
|
+
parties on a paid basis, including through paid support
|
|
17
|
+
arrangements, that significantly overlaps with the capabilities
|
|
18
|
+
of RADLAB LLC's paid version(s) of the Licensed Work. If Your
|
|
19
|
+
Product is not a competitive offering when You first make it
|
|
20
|
+
generally available, it will not become a competitive offering
|
|
21
|
+
later due to RADLAB LLC releasing a new version of the Licensed
|
|
22
|
+
Work with additional capabilities. In addition, Products that
|
|
23
|
+
are not provided on a paid basis are not competitive.
|
|
24
|
+
|
|
25
|
+
"Product" means software that is offered to end users to manage
|
|
26
|
+
in their own environments or offered as a service on a hosted
|
|
27
|
+
basis.
|
|
28
|
+
|
|
29
|
+
"Embedded" means including the source code or executable code
|
|
30
|
+
from the Licensed Work in a competitive offering. "Embedded"
|
|
31
|
+
also means packaging the competitive offering in such a way
|
|
32
|
+
that the Licensed Work must be accessed or downloaded for the
|
|
33
|
+
competitive offering to operate.
|
|
34
|
+
|
|
35
|
+
Hosting or using the Licensed Work(s) for internal purposes
|
|
36
|
+
within an organization is not considered a competitive
|
|
37
|
+
offering. RADLAB LLC considers your organization to include all
|
|
38
|
+
of your affiliates under common control.
|
|
39
|
+
|
|
40
|
+
For binding interpretive guidance on using RADLAB LLC products
|
|
41
|
+
under the Business Source License, please see the LICENSE_FAQ
|
|
42
|
+
at https://github.com/getbourdon/bourdon/blob/main/LICENSE_FAQ.md
|
|
43
|
+
Change Date: Four years from the date the Licensed Work is published.
|
|
44
|
+
Change License: Apache License, Version 2.0
|
|
45
|
+
|
|
46
|
+
For information about alternative licensing arrangements for the Licensed Work,
|
|
47
|
+
please contact licensing@bourdon.ai.
|
|
48
|
+
|
|
49
|
+
Notice
|
|
50
|
+
|
|
51
|
+
Business Source License 1.1
|
|
52
|
+
|
|
53
|
+
Terms
|
|
54
|
+
|
|
55
|
+
The Licensor hereby grants you the right to copy, modify, create derivative
|
|
56
|
+
works, redistribute, and make non-production use of the Licensed Work. The
|
|
57
|
+
Licensor may make an Additional Use Grant, above, permitting limited production use.
|
|
58
|
+
|
|
59
|
+
Effective on the Change Date, or the fourth anniversary of the first publicly
|
|
60
|
+
available distribution of a specific version of the Licensed Work under this
|
|
61
|
+
License, whichever comes first, the Licensor hereby grants you rights under
|
|
62
|
+
the terms of the Change License, and the rights granted in the paragraph
|
|
63
|
+
above terminate.
|
|
64
|
+
|
|
65
|
+
If your use of the Licensed Work does not comply with the requirements
|
|
66
|
+
currently in effect as described in this License, you must purchase a
|
|
67
|
+
commercial license from the Licensor, its affiliated entities, or authorized
|
|
68
|
+
resellers, or you must refrain from using the Licensed Work.
|
|
69
|
+
|
|
70
|
+
All copies of the original and modified Licensed Work, and derivative works
|
|
71
|
+
of the Licensed Work, are subject to this License. This License applies
|
|
72
|
+
separately for each version of the Licensed Work and the Change Date may vary
|
|
73
|
+
for each version of the Licensed Work released by Licensor.
|
|
74
|
+
|
|
75
|
+
You must conspicuously display this License on each original or modified copy
|
|
76
|
+
of the Licensed Work. If you receive the Licensed Work in original or
|
|
77
|
+
modified form from a third party, the terms and conditions set forth in this
|
|
78
|
+
License apply to your use of that work.
|
|
79
|
+
|
|
80
|
+
Any use of the Licensed Work in violation of this License will automatically
|
|
81
|
+
terminate your rights under this License for the current and all other
|
|
82
|
+
versions of the Licensed Work.
|
|
83
|
+
|
|
84
|
+
This License does not grant you any right in any trademark or logo of
|
|
85
|
+
Licensor or its affiliates (provided that you may use a trademark or logo of
|
|
86
|
+
Licensor as expressly required by this License).
|
|
87
|
+
|
|
88
|
+
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
|
|
89
|
+
AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
|
|
90
|
+
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
|
|
91
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
|
|
92
|
+
TITLE.
|
package/LICENSE_FAQ.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Bourdon License FAQ
|
|
2
|
+
|
|
3
|
+
This document explains how the Business Source License (BSL) 1.1 applies to Bourdon. It is interpretive guidance — the [LICENSE](LICENSE) itself is the authoritative legal text.
|
|
4
|
+
|
|
5
|
+
## TL;DR
|
|
6
|
+
|
|
7
|
+
- **Free for almost everyone:** solo developers, internal use at any company, non-commercial projects, research, education
|
|
8
|
+
- **Free for non-competing commercial use:** building a product on top of Bourdon that doesn't directly compete with RADLAB LLC's hosted Bourdon offering
|
|
9
|
+
- **Requires a commercial license:** running Bourdon as a hosted/embedded service that competes with RADLAB LLC's paid offerings
|
|
10
|
+
- **Auto-converts to Apache 2.0:** four years after each version is published, that version becomes pure Apache 2.0 (fully OSS forever)
|
|
11
|
+
|
|
12
|
+
## Why BSL instead of MIT?
|
|
13
|
+
|
|
14
|
+
Bourdon was originally MIT-licensed (v0.0.1 through v0.1.0). The rename from Continuo → Bourdon (2026-05-05) was the right moment to also relicense before the project gained external adoption — the project's strategy depends on RADLAB LLC capturing some commercial value to fund continued development. Pure MIT meant any cloud company could offer Bourdon-as-a-Service without contributing back, leaving the maintainer with no path to sustainability.
|
|
15
|
+
|
|
16
|
+
BSL 1.1 keeps Bourdon source-available and free for the vast majority of users while preserving a commercial wedge against direct hosted-service competitors. HashiCorp (Terraform, Vault), CockroachDB, Sentry, Couchbase, and MariaDB all use this license; HashiCorp was acquired by IBM under BSL in 2024, demonstrating it doesn't kill commercial outcomes.
|
|
17
|
+
|
|
18
|
+
## Common scenarios
|
|
19
|
+
|
|
20
|
+
### ✅ I'm a developer using Bourdon to add memory to my own AI agent
|
|
21
|
+
**Free.** Solo / internal use is explicitly permitted by the Additional Use Grant.
|
|
22
|
+
|
|
23
|
+
### ✅ My company uses Bourdon internally for our own engineering tools
|
|
24
|
+
**Free.** "Hosting or using the Licensed Work(s) for internal purposes within an organization is not considered a competitive offering."
|
|
25
|
+
|
|
26
|
+
### ✅ I'm building a SaaS product that uses Bourdon under the hood
|
|
27
|
+
**Probably free** — depends on whether your product is "competitive." If your product's main value is something else (e.g., a CRM that happens to use memory federation internally), you're fine. If your product *is* memory-federation-as-a-service marketed against Bourdon, you'd need a commercial license.
|
|
28
|
+
|
|
29
|
+
The BSL Additional Use Grant defines "competitive offering" specifically: a product offered to third parties on a paid basis that *significantly overlaps with the capabilities of RADLAB LLC's paid version(s) of the Licensed Work*. If RADLAB LLC has no paid version yet, then there's nothing to overlap with — but a future paid version doesn't retroactively make existing non-competing products competitive (per the explicit grandfathering language in the grant).
|
|
30
|
+
|
|
31
|
+
### ✅ I want to fork Bourdon and build a different memory system on top
|
|
32
|
+
**Free**, as long as you're not running it as a competing hosted service. Standard OSS-style fork-and-modify is permitted.
|
|
33
|
+
|
|
34
|
+
### ✅ I'm contributing back to Bourdon via a PR
|
|
35
|
+
**Free.** Contribution doesn't trigger any commercial concerns.
|
|
36
|
+
|
|
37
|
+
### ⚠️ I'm Anthropic / OpenAI / Google / a major AI vendor and want to integrate Bourdon natively into my product
|
|
38
|
+
This is where you'd want to talk to RADLAB LLC. Native integration into a paid AI product *probably* counts as embedding under the BSL definition. A commercial license is straightforward to negotiate.
|
|
39
|
+
|
|
40
|
+
Contact: licensing@bourdon.ai
|
|
41
|
+
|
|
42
|
+
### ⚠️ I'm running a managed Bourdon hosted service for paying customers
|
|
43
|
+
Commercial license needed. Same contact as above.
|
|
44
|
+
|
|
45
|
+
## Auto-conversion timeline
|
|
46
|
+
|
|
47
|
+
Each version of Bourdon auto-converts to **Apache License 2.0** four years after its first publication:
|
|
48
|
+
|
|
49
|
+
| Version | Published | Apache 2.0 conversion |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| v0.2.0 | 2026-05-06 | 2030-05-06 |
|
|
52
|
+
| v0.3.0 | 2026-05-09 | 2030-05-09 |
|
|
53
|
+
| v0.4.0 | 2026-05-10 | 2030-05-10 |
|
|
54
|
+
| v0.4.1 | 2026-05-10 | 2030-05-10 |
|
|
55
|
+
| v0.5.0 | 2026-05-11 | 2030-05-11 |
|
|
56
|
+
| v0.6.0 | 2026-05-11 | 2030-05-11 |
|
|
57
|
+
| ... | ... | ... |
|
|
58
|
+
|
|
59
|
+
After conversion, that version is fully OSS forever. The BSL only protects the *current* version; the long tail becomes Apache 2.0 over time.
|
|
60
|
+
|
|
61
|
+
## What about old versions (v0.0.1 through v0.1.0)?
|
|
62
|
+
|
|
63
|
+
Versions v0.0.1 through v0.1.0 were published under MIT. **Anyone who downloaded those versions has perpetual MIT rights to those specific releases.** Relicensing applies forward only, not backward — that's how OSS license changes always work.
|
|
64
|
+
|
|
65
|
+
## Frequently confused points
|
|
66
|
+
|
|
67
|
+
### "BSL isn't OSI-approved, so it's not 'real OSS'"
|
|
68
|
+
True that BSL 1.1 isn't on the OSI-approved list, but it's source-available and auto-converts to Apache 2.0 (which IS OSI-approved). The OSS Initiative's narrow approval list is one definition of "open"; BSL meets a broader practical definition (code is public, can be read, modified, used freely for most purposes, and becomes OSI-approved over time).
|
|
69
|
+
|
|
70
|
+
### "I can't use BSL code in my company because of policy bans"
|
|
71
|
+
Some enterprise software policies are written against AGPL (a different copyleft license) and don't actually cover BSL. Worth checking with your legal team — most BSL projects have been accepted in environments that ban AGPL.
|
|
72
|
+
|
|
73
|
+
### "What if I'm not sure whether my use is 'competitive'?"
|
|
74
|
+
Email licensing@bourdon.ai with a description of what you're building. RADLAB LLC will give you a written interpretation. Better to ask than to guess.
|
|
75
|
+
|
|
76
|
+
## Contact
|
|
77
|
+
|
|
78
|
+
Licensing questions: **licensing@bourdon.ai**
|
|
79
|
+
General project questions: GitHub issues at [github.com/getbourdon/bourdon/issues](https://github.com/getbourdon/bourdon/issues)
|
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @getbourdon/participants
|
|
2
|
+
|
|
3
|
+
The external-agent → L5 reader layer of [Bourdon](https://bourdon.ai) (BUSL-1.1).
|
|
4
|
+
|
|
5
|
+
A **participant** normalizes a foreign agent's native memory store into a
|
|
6
|
+
visibility-filtered, redacted [L5 manifest](../l5). It is the bridge between an
|
|
7
|
+
agent's private, vendor-specific state and the cross-machine federation library.
|
|
8
|
+
|
|
9
|
+
Python (`pip install bourdon`) is the oracle; this TS mirror asserts against the
|
|
10
|
+
`@getbourdon/conformance` `native_stores` fixtures (output shape only).
|
|
11
|
+
|
|
12
|
+
## Reader categories
|
|
13
|
+
|
|
14
|
+
| Reader | Category | Native store |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| `HermesParticipant` | SQLite (read-only) | `~/.hermes/` — `state.db` sessions + `memories/*.md` |
|
|
17
|
+
| `ClaudeCodeParticipant` | file / convention | claude-brain + `~/.claude/projects/*/memory/` + KG JSONL |
|
|
18
|
+
| `GitHubCopilotParticipant` | network (TTL cache) | `api.github.com` PR activity → `~/.cache/bourdon/github-copilot/` |
|
|
19
|
+
|
|
20
|
+
The heavier SQLite readers (`cursor`, `codex`, `copilot_cli`) are **deferred to
|
|
21
|
+
the follow-on slice**; `openclaw` ships as the `@getbourdon/openclaw` plugin.
|
|
22
|
+
|
|
23
|
+
## The four invariants (enforced in code, not by trust)
|
|
24
|
+
|
|
25
|
+
1. **Visibility filter BEFORE emission** — every entity passes
|
|
26
|
+
`filterForFederation`; `private_tags` always win. A PRIVATE entity never lands
|
|
27
|
+
in a manifest.
|
|
28
|
+
2. **Deterministic / idempotent `exportL5`** — same store → byte-identical
|
|
29
|
+
`toDict` (L6 hashes it for change detection).
|
|
30
|
+
3. **`healthCheck` NEVER throws** — a diagnostic must never crash `bourdon doctor`.
|
|
31
|
+
4. **Every native string through `@getbourdon/redaction`** before it enters L5.
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { discoverParticipants, toDict } from "@getbourdon/participants";
|
|
37
|
+
|
|
38
|
+
for (const p of discoverParticipants()) {
|
|
39
|
+
const health = p.healthCheck(); // never throws
|
|
40
|
+
if (health.status === "blocked") continue;
|
|
41
|
+
const manifest = p.exportL5(); // visibility-filtered, redacted, deterministic
|
|
42
|
+
console.log(p.agentId, toDict(manifest));
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Discovery is a **static registry** (`FIRST_PARTY`), not a filesystem package
|
|
47
|
+
scan — it preserves the Python oracle's `agentId`-sorted output, first-wins
|
|
48
|
+
dedupe, and log-and-skip-on-construction-failure resilience. Third-party plugins
|
|
49
|
+
are gated behind `BOURDON_PLUGINS=1` (a no-op stub today).
|
|
50
|
+
|
|
51
|
+
## Native addon note
|
|
52
|
+
|
|
53
|
+
The SQLite readers use [`better-sqlite3`](https://github.com/WiseLibs/better-sqlite3),
|
|
54
|
+
a native addon. It is loaded lazily: if the addon fails to build on a given
|
|
55
|
+
toolchain, `sqliteAvailable()` returns `false`, the SQLite readers degrade
|
|
56
|
+
gracefully, and the rest of the package (file + network readers) keeps working.
|
|
57
|
+
The SQLite-reader parity tests are gated on `sqliteAvailable()`.
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
BUSL-1.1 — see [LICENSE](./LICENSE) and [LICENSE_FAQ.md](./LICENSE_FAQ.md).
|
package/dist/base.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bourdon participant base — the Protocol → TS interface, errors, and the
|
|
3
|
+
* AgentStore / HealthStatus types. Faithful port of `participants/base.py`.
|
|
4
|
+
*
|
|
5
|
+
* The L5 dataclass model (AgentInfo/Entity/Session/VisibilityPolicy/L5Manifest),
|
|
6
|
+
* the `toDict` omission rules, and the visibility helpers all live in
|
|
7
|
+
* `@getbourdon/l5` and are reused verbatim — only THAT output shape is the parity
|
|
8
|
+
* contract. This module owns the participant CONTRACT surface around them.
|
|
9
|
+
*
|
|
10
|
+
* Contract semantics (enforced by every reader, asserted by the conformance
|
|
11
|
+
* fixtures, never by trust):
|
|
12
|
+
* 1. visibility filter BEFORE emission (`filterForFederation`) — private wins.
|
|
13
|
+
* 2. deterministic / idempotent `exportL5` — same store → identical `toDict`.
|
|
14
|
+
* 3. `healthCheck` NEVER throws — catch-all → degraded/blocked.
|
|
15
|
+
* 4. every native string through `@getbourdon/redaction` before it enters L5.
|
|
16
|
+
*/
|
|
17
|
+
import type { L5ManifestModel, SessionModel } from "@getbourdon/l5";
|
|
18
|
+
/** Tied to Bourdon spec v0.1 — same literals as the Python oracle. */
|
|
19
|
+
export declare const CONTRACT_VERSION = "0.1";
|
|
20
|
+
export declare const SPEC_VERSION = "0.1";
|
|
21
|
+
/** Base class for participant errors. */
|
|
22
|
+
export declare class ParticipantError extends Error {
|
|
23
|
+
constructor(message?: string);
|
|
24
|
+
}
|
|
25
|
+
/** Raised by `discover()` when the native store cannot be found or read.
|
|
26
|
+
* Non-fatal: L6 skips this agent rather than aborting the whole export. */
|
|
27
|
+
export declare class ParticipantDiscoveryError extends ParticipantError {
|
|
28
|
+
}
|
|
29
|
+
/** Raised by `exportL5()` / `exportSessions()` when an export fails mid-op
|
|
30
|
+
* (the store WAS present — distinct from a discovery miss). */
|
|
31
|
+
export declare class ParticipantExportError extends ParticipantError {
|
|
32
|
+
}
|
|
33
|
+
/** Raised when the native store's version is outside the supported range.
|
|
34
|
+
* Subtype of discovery error (a version mismatch is a discovery-time miss). */
|
|
35
|
+
export declare class ParticipantVersionMismatchError extends ParticipantDiscoveryError {
|
|
36
|
+
}
|
|
37
|
+
/** Metadata describing the native agent store. Returned by `discover()`. */
|
|
38
|
+
export interface AgentStore {
|
|
39
|
+
path: string;
|
|
40
|
+
version: string;
|
|
41
|
+
metadata: Record<string, unknown>;
|
|
42
|
+
}
|
|
43
|
+
/** Returned by `healthCheck()`. Consumed by the `bourdon doctor` CLI. */
|
|
44
|
+
export interface HealthStatus {
|
|
45
|
+
status: "ok" | "degraded" | "blocked";
|
|
46
|
+
reason?: string;
|
|
47
|
+
details: Record<string, unknown>;
|
|
48
|
+
/** A human-runnable command to remedy a non-ok status. */
|
|
49
|
+
proposedFix?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Structural interface every participant must satisfy (the Python
|
|
53
|
+
* `@runtime_checkable Protocol`). Duck-typed at registration: a class is a
|
|
54
|
+
* participant iff it has `agentId` + `agentType` + `exportL5` + `healthCheck`.
|
|
55
|
+
*
|
|
56
|
+
* Optional members (`displayName`, a `defaultNativePath` static) are documented
|
|
57
|
+
* here as in the Python Protocol; discovery + the setup wizard fall back to
|
|
58
|
+
* sensible defaults when absent.
|
|
59
|
+
*/
|
|
60
|
+
export interface BourdonParticipant {
|
|
61
|
+
agentId: string;
|
|
62
|
+
agentType: string;
|
|
63
|
+
nativePath: string;
|
|
64
|
+
/** Human-friendly label (e.g. "GitHub Copilot"). Optional. */
|
|
65
|
+
displayName?: string;
|
|
66
|
+
/** Confirm the native store exists; return metadata. Throws
|
|
67
|
+
* {@link ParticipantDiscoveryError} when missing/unreadable. */
|
|
68
|
+
discover(): AgentStore;
|
|
69
|
+
/** Build the L5 manifest from native memory, applying the visibility filter
|
|
70
|
+
* BEFORE return. Deterministic — same store yields the same manifest. */
|
|
71
|
+
exportL5(since?: Date): L5ManifestModel;
|
|
72
|
+
/** Export recent sessions in normalized schema. */
|
|
73
|
+
exportSessions(since?: Date, limit?: number): SessionModel[];
|
|
74
|
+
/** Return ok / degraded / blocked with a reason. MUST NOT throw. */
|
|
75
|
+
healthCheck(): HealthStatus;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Tiny structural guard mirroring Python's `_PARTICIPANT_MARKER_ATTRS`
|
|
79
|
+
* duck-type: a value is a participant iff it carries the four marker members.
|
|
80
|
+
*/
|
|
81
|
+
export declare function isParticipant(obj: unknown): obj is BourdonParticipant;
|
package/dist/base.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bourdon participant base — the Protocol → TS interface, errors, and the
|
|
3
|
+
* AgentStore / HealthStatus types. Faithful port of `participants/base.py`.
|
|
4
|
+
*
|
|
5
|
+
* The L5 dataclass model (AgentInfo/Entity/Session/VisibilityPolicy/L5Manifest),
|
|
6
|
+
* the `toDict` omission rules, and the visibility helpers all live in
|
|
7
|
+
* `@getbourdon/l5` and are reused verbatim — only THAT output shape is the parity
|
|
8
|
+
* contract. This module owns the participant CONTRACT surface around them.
|
|
9
|
+
*
|
|
10
|
+
* Contract semantics (enforced by every reader, asserted by the conformance
|
|
11
|
+
* fixtures, never by trust):
|
|
12
|
+
* 1. visibility filter BEFORE emission (`filterForFederation`) — private wins.
|
|
13
|
+
* 2. deterministic / idempotent `exportL5` — same store → identical `toDict`.
|
|
14
|
+
* 3. `healthCheck` NEVER throws — catch-all → degraded/blocked.
|
|
15
|
+
* 4. every native string through `@getbourdon/redaction` before it enters L5.
|
|
16
|
+
*/
|
|
17
|
+
/** Tied to Bourdon spec v0.1 — same literals as the Python oracle. */
|
|
18
|
+
export const CONTRACT_VERSION = "0.1";
|
|
19
|
+
export const SPEC_VERSION = "0.1";
|
|
20
|
+
// -- Errors -------------------------------------------------------------------
|
|
21
|
+
/** Base class for participant errors. */
|
|
22
|
+
export class ParticipantError extends Error {
|
|
23
|
+
constructor(message) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = new.target.name;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** Raised by `discover()` when the native store cannot be found or read.
|
|
29
|
+
* Non-fatal: L6 skips this agent rather than aborting the whole export. */
|
|
30
|
+
export class ParticipantDiscoveryError extends ParticipantError {
|
|
31
|
+
}
|
|
32
|
+
/** Raised by `exportL5()` / `exportSessions()` when an export fails mid-op
|
|
33
|
+
* (the store WAS present — distinct from a discovery miss). */
|
|
34
|
+
export class ParticipantExportError extends ParticipantError {
|
|
35
|
+
}
|
|
36
|
+
/** Raised when the native store's version is outside the supported range.
|
|
37
|
+
* Subtype of discovery error (a version mismatch is a discovery-time miss). */
|
|
38
|
+
export class ParticipantVersionMismatchError extends ParticipantDiscoveryError {
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Tiny structural guard mirroring Python's `_PARTICIPANT_MARKER_ATTRS`
|
|
42
|
+
* duck-type: a value is a participant iff it carries the four marker members.
|
|
43
|
+
*/
|
|
44
|
+
export function isParticipant(obj) {
|
|
45
|
+
if (obj === null || typeof obj !== "object")
|
|
46
|
+
return false;
|
|
47
|
+
const o = obj;
|
|
48
|
+
return (typeof o["agentId"] === "string" &&
|
|
49
|
+
typeof o["agentType"] === "string" &&
|
|
50
|
+
typeof o["exportL5"] === "function" &&
|
|
51
|
+
typeof o["healthCheck"] === "function");
|
|
52
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @getbourdon/participants — the external-agent → L5 reader layer (BUSL-1.1).
|
|
3
|
+
*
|
|
4
|
+
* Each participant normalizes a foreign agent's native memory store into a
|
|
5
|
+
* visibility-filtered, redacted L5 manifest. Three reader categories:
|
|
6
|
+
* - SQLite (read-only, better-sqlite3 mode=ro): {@link HermesParticipant}
|
|
7
|
+
* - file / convention: {@link ClaudeCodeParticipant}
|
|
8
|
+
* - network (TTL cache + lazy AuthProvider): {@link GitHubCopilotParticipant}
|
|
9
|
+
*
|
|
10
|
+
* Four invariants enforced in code, not by trust: visibility-filter-before-
|
|
11
|
+
* emission, deterministic exportL5, healthCheck never throws, redact every
|
|
12
|
+
* native string. Discovery is a static registry ({@link discoverParticipants}).
|
|
13
|
+
*
|
|
14
|
+
* Python (`pip install bourdon`) is the oracle; this mirror asserts against the
|
|
15
|
+
* @getbourdon/conformance `native_stores` fixtures (output shape only).
|
|
16
|
+
* Follow-on slice: cursor + codex (heavier SQLite) + copilot_cli; openclaw ships
|
|
17
|
+
* as the @getbourdon/openclaw plugin.
|
|
18
|
+
*/
|
|
19
|
+
export { CONTRACT_VERSION, SPEC_VERSION, ParticipantError, ParticipantDiscoveryError, ParticipantExportError, ParticipantVersionMismatchError, isParticipant, type AgentStore, type BourdonParticipant, type HealthStatus, } from "./base.js";
|
|
20
|
+
export { sqliteAvailable, openReadonly, tryOpenReadonly, tableExists, tableColumns, epochToIsoDate, epochToIsoDatetime, projectKeyFromCwd, friendlyLabel, type OpenReadonlyOptions, type SqliteDatabase, } from "./sqlite-base.js";
|
|
21
|
+
export { NETWORK_CONTRACT_VERSION, DEFAULT_CACHE_TTL_SECONDS, NetworkUnavailable, ParticipantAuthError, NetworkParticipant, PayloadCache, CacheEntry, envAuthProvider, defaultCacheRoot, type AuthProvider, type NetworkParticipantOptions, } from "./network-base.js";
|
|
22
|
+
export { HermesParticipant } from "./readers/hermes.js";
|
|
23
|
+
export { ClaudeCodeParticipant } from "./readers/claude-code.js";
|
|
24
|
+
export { GitHubCopilotParticipant, ghTokenProvider } from "./readers/github-copilot.js";
|
|
25
|
+
export { FIRST_PARTY, discoverParticipants, resolvePluginParticipants, type ParticipantCtor, } from "./registry.js";
|
|
26
|
+
export { Visibility, applyVisibility, filterForFederation, toDict, validateManifest, type EntityModel, type L5ManifestModel, type SessionModel, type VisibilityPolicyModel, } from "@getbourdon/l5";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @getbourdon/participants — the external-agent → L5 reader layer (BUSL-1.1).
|
|
3
|
+
*
|
|
4
|
+
* Each participant normalizes a foreign agent's native memory store into a
|
|
5
|
+
* visibility-filtered, redacted L5 manifest. Three reader categories:
|
|
6
|
+
* - SQLite (read-only, better-sqlite3 mode=ro): {@link HermesParticipant}
|
|
7
|
+
* - file / convention: {@link ClaudeCodeParticipant}
|
|
8
|
+
* - network (TTL cache + lazy AuthProvider): {@link GitHubCopilotParticipant}
|
|
9
|
+
*
|
|
10
|
+
* Four invariants enforced in code, not by trust: visibility-filter-before-
|
|
11
|
+
* emission, deterministic exportL5, healthCheck never throws, redact every
|
|
12
|
+
* native string. Discovery is a static registry ({@link discoverParticipants}).
|
|
13
|
+
*
|
|
14
|
+
* Python (`pip install bourdon`) is the oracle; this mirror asserts against the
|
|
15
|
+
* @getbourdon/conformance `native_stores` fixtures (output shape only).
|
|
16
|
+
* Follow-on slice: cursor + codex (heavier SQLite) + copilot_cli; openclaw ships
|
|
17
|
+
* as the @getbourdon/openclaw plugin.
|
|
18
|
+
*/
|
|
19
|
+
// Contract surface: Protocol interface, errors, AgentStore + HealthStatus.
|
|
20
|
+
export { CONTRACT_VERSION, SPEC_VERSION, ParticipantError, ParticipantDiscoveryError, ParticipantExportError, ParticipantVersionMismatchError, isParticipant, } from "./base.js";
|
|
21
|
+
// SQLite read-only primitives.
|
|
22
|
+
export { sqliteAvailable, openReadonly, tryOpenReadonly, tableExists, tableColumns, epochToIsoDate, epochToIsoDatetime, projectKeyFromCwd, friendlyLabel, } from "./sqlite-base.js";
|
|
23
|
+
// Network base: TTL cache, AuthProvider, degrade-to-stale-cache, auth boundary.
|
|
24
|
+
export { NETWORK_CONTRACT_VERSION, DEFAULT_CACHE_TTL_SECONDS, NetworkUnavailable, ParticipantAuthError, NetworkParticipant, PayloadCache, CacheEntry, envAuthProvider, defaultCacheRoot, } from "./network-base.js";
|
|
25
|
+
// Readers.
|
|
26
|
+
export { HermesParticipant } from "./readers/hermes.js";
|
|
27
|
+
export { ClaudeCodeParticipant } from "./readers/claude-code.js";
|
|
28
|
+
export { GitHubCopilotParticipant, ghTokenProvider } from "./readers/github-copilot.js";
|
|
29
|
+
// Discovery (static registry).
|
|
30
|
+
export { FIRST_PARTY, discoverParticipants, resolvePluginParticipants, } from "./registry.js";
|
|
31
|
+
// Re-exported from @getbourdon/l5 for convenience (the L5 model + serialization
|
|
32
|
+
// the readers build on; only this output shape is the parity contract).
|
|
33
|
+
export { Visibility, applyVisibility, filterForFederation, toDict, validateManifest, } from "@getbourdon/l5";
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network-shaped participant base — port of `participants/_network_base.py`
|
|
3
|
+
* (v0.3 adapter contract). Same `exportL5` / `exportSessions` half as a file
|
|
4
|
+
* participant; only the discovery half differs (fetch from an authenticated
|
|
5
|
+
* API). The base provides the three guarantees so adapters don't reimplement:
|
|
6
|
+
*
|
|
7
|
+
* 1. Local TTL cache under `~/.cache/bourdon/<slug>/payload.json` storing ONLY
|
|
8
|
+
* `{fetched_at, payload}` — NEVER the token. A cache hit = zero network.
|
|
9
|
+
* 2. Graceful degradation: network down → serve the most recent cached payload
|
|
10
|
+
* (even expired) and report `degraded` (not silence).
|
|
11
|
+
* 3. Auth boundary: an injected lazy {@link AuthProvider} (env / keychain).
|
|
12
|
+
* An invalid token (401/403 non-ratelimit) ALWAYS propagates as `blocked`
|
|
13
|
+
* and is NEVER masked by stale cache; a missing token serves stale when a
|
|
14
|
+
* cache exists, else blocks; rate-limit degrades.
|
|
15
|
+
*
|
|
16
|
+
* A subclass sets `participantSlug` / `agentId` / `agentType` (+ `nativePath`)
|
|
17
|
+
* and implements `fetchPayload(token)` + `payloadToL5(payload)` (pure, no I/O).
|
|
18
|
+
*/
|
|
19
|
+
import type { L5ManifestModel, SessionModel } from "@getbourdon/l5";
|
|
20
|
+
import { ParticipantError, type AgentStore, type BourdonParticipant, type HealthStatus } from "./base.js";
|
|
21
|
+
export declare const NETWORK_CONTRACT_VERSION = "0.3";
|
|
22
|
+
/** Default cache TTL — adapters override via `cacheTtlSeconds`. */
|
|
23
|
+
export declare const DEFAULT_CACHE_TTL_SECONDS: number;
|
|
24
|
+
/** Transport failure / timeout / 5xx / rate-limit — recoverable via cache fallback. */
|
|
25
|
+
export declare class NetworkUnavailable extends ParticipantError {
|
|
26
|
+
}
|
|
27
|
+
/** 401/403 / missing credential — NOT recoverable via cache. The user must fix
|
|
28
|
+
* auth; surfaced as `blocked`, never silently served from stale cache (stale
|
|
29
|
+
* data would mask a broken token indefinitely). */
|
|
30
|
+
export declare class ParticipantAuthError extends ParticipantError {
|
|
31
|
+
}
|
|
32
|
+
/** A zero-arg callable returning the token string, or null if no credential is
|
|
33
|
+
* available. Kept a callable (not a bare string) so the token is fetched lazily
|
|
34
|
+
* at call time and never sits in the participant's fields or the cache. */
|
|
35
|
+
export type AuthProvider = () => string | null;
|
|
36
|
+
/** Auth provider that reads a token from an environment variable. */
|
|
37
|
+
export declare function envAuthProvider(varName: string): AuthProvider;
|
|
38
|
+
/** `$XDG_CACHE_HOME/bourdon` or `~/.cache/bourdon`. */
|
|
39
|
+
export declare function defaultCacheRoot(): string;
|
|
40
|
+
/** A cached payload + the wall-clock time it was fetched (epoch seconds). */
|
|
41
|
+
export declare class CacheEntry {
|
|
42
|
+
readonly fetchedAt: number;
|
|
43
|
+
readonly payload: Record<string, unknown>;
|
|
44
|
+
constructor(fetchedAt: number, payload: Record<string, unknown>);
|
|
45
|
+
get ageSeconds(): number;
|
|
46
|
+
isFresh(ttlSeconds: number): boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A tiny JSON file cache, one file per participant slug. Stores only
|
|
50
|
+
* `{fetched_at, payload}` — never the auth token. Read/write are best-effort: a
|
|
51
|
+
* corrupt or unwritable cache degrades to "no cache", never raises.
|
|
52
|
+
*/
|
|
53
|
+
export declare class PayloadCache {
|
|
54
|
+
readonly slug: string;
|
|
55
|
+
readonly root: string;
|
|
56
|
+
readonly path: string;
|
|
57
|
+
constructor(slug: string, rootDir?: string);
|
|
58
|
+
read(): CacheEntry | null;
|
|
59
|
+
write(payload: Record<string, unknown>): void;
|
|
60
|
+
}
|
|
61
|
+
export interface NetworkParticipantOptions {
|
|
62
|
+
authProvider?: AuthProvider | null;
|
|
63
|
+
cacheRoot?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Base for participants whose native state lives behind an authenticated API.
|
|
67
|
+
* Caching, degradation, the auth boundary, and the
|
|
68
|
+
* `discover`/`exportL5`/`exportSessions`/`healthCheck` plumbing live here;
|
|
69
|
+
* subclasses provide only `fetchPayload` + `payloadToL5`.
|
|
70
|
+
*/
|
|
71
|
+
export declare abstract class NetworkParticipant implements BourdonParticipant {
|
|
72
|
+
participantSlug: string;
|
|
73
|
+
agentId: string;
|
|
74
|
+
agentType: string;
|
|
75
|
+
cacheTtlSeconds: number;
|
|
76
|
+
nativePath: string;
|
|
77
|
+
protected readonly authProvider: AuthProvider | null;
|
|
78
|
+
private readonly cacheRoot?;
|
|
79
|
+
private cacheInstance?;
|
|
80
|
+
constructor(opts?: NetworkParticipantOptions);
|
|
81
|
+
/** Lazily built so it reads the SUBCLASS's `participantSlug` — subclass field
|
|
82
|
+
* initializers run after `super()`, so building the cache in the constructor
|
|
83
|
+
* would capture the base "network" slug instead. */
|
|
84
|
+
protected get cache(): PayloadCache;
|
|
85
|
+
/** Do the authenticated API call; return a JSON-serializable dict. Raise
|
|
86
|
+
* {@link NetworkUnavailable} on transport/5xx/rate-limit (→ cache fallback) or
|
|
87
|
+
* {@link ParticipantAuthError} on 401/403 (→ blocked, no fallback). */
|
|
88
|
+
abstract fetchPayload(token: string): Record<string, unknown>;
|
|
89
|
+
/** Normalize a (fresh or cached) payload into an L5 manifest. Pure, no I/O. */
|
|
90
|
+
abstract payloadToL5(payload: Record<string, unknown>): L5ManifestModel;
|
|
91
|
+
/**
|
|
92
|
+
* Resolve (payload, source) where source is 'cache' | 'network' | 'stale-cache'.
|
|
93
|
+
* 1. Fresh cache hit → 'cache' (no network)
|
|
94
|
+
* 2. Miss/expired + token → 'network' (fetch + refresh cache)
|
|
95
|
+
* 3. Net fails + any cache → 'stale-cache' (serve last good payload)
|
|
96
|
+
* 4. Net fails + no cache → raise
|
|
97
|
+
* Auth errors are never swallowed — they propagate so health surfaces
|
|
98
|
+
* 'blocked' rather than serving stale data behind a dead token.
|
|
99
|
+
*/
|
|
100
|
+
protected getPayload(): {
|
|
101
|
+
payload: Record<string, unknown>;
|
|
102
|
+
source: string;
|
|
103
|
+
};
|
|
104
|
+
discover(): AgentStore;
|
|
105
|
+
exportL5(_since?: Date): L5ManifestModel;
|
|
106
|
+
exportSessions(since?: Date, limit?: number): SessionModel[];
|
|
107
|
+
healthCheck(): HealthStatus;
|
|
108
|
+
}
|