Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@ All notable changes to **basou** are recorded here. The project follows

### Added

- `@basou/sdk` v0.2 — the package gains a runtime, read-only programmatic API
for reading a workspace's provenance (it was types-only before). It is a
thin, ergonomic, semver-stable facade over `@basou/core`'s readers, so
third-party tooling can read `.basou/` without stitching low-level calls
together and without any risk of mutating provenance (no writers are
exposed). `openWorkspace(repoRoot, options?)` returns a `Workspace` handle —
`repoRoot` is any directory holding a `.basou/` (no git required);
`resolveWorkspaceRoot(cwd)` is a git-based convenience for finding it. The
handle exposes `manifest()`, `status()`, `listSessions()` /
`getSession(idOrPrefix)`, `readEvents(idOrPrefix)` /
`streamEvents(idOrPrefix)`, `listTasks()` / `getTask(idOrPrefix)`,
`listApprovals()` / `getApproval(id)`, `stats(options?)`, and
`renderHandoff()` / `renderDecisions()`. Session, task, and event lookups
accept a unique prefix (matching nothing yields `null`, matching more than
one throws `AmbiguousIdError`); `getApproval` takes an exact id. A missing or
invalid `.basou/` throws `WorkspaceNotFoundError`. A `now` clock is injectable
for deterministic reads.
Read types (`Session`, `Event`, `Task`, `WorkStatsResult`, etc.) are
re-exported so consumers import only from `@basou/sdk`. `BASOU_SDK_VERSION`
is now `0.2.0`.
- `session.metrics.machine_active_time_ms` (additive, optional) — model compute
time: the summed duration of a source's per-turn spans (Codex
`task_complete.duration_ms`). A SUBSET of a session's active time, kept as a
Expand Down
7 changes: 5 additions & 2 deletions packages/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
"version": "0.5.0",
"type": "module",
"license": "Apache-2.0",
"description": "Type-only SDK for Basou adapter authors. v0.1: types only, no runtime.",
"description": "Read-only SDK for Basou: a stable, ergonomic API to read a workspace's provenance (sessions, events, tasks, approvals, stats).",
"author": "Takashi Matsuyama",
"keywords": [
"ai",
"agent",
"sdk",
"adapter",
"provenance",
"basou"
],
"repository": {
Expand Down Expand Up @@ -41,6 +41,9 @@
"engines": {
"node": ">=20.10.0"
},
"dependencies": {
"@basou/core": "workspace:*"
},
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
Expand Down
41 changes: 41 additions & 0 deletions packages/sdk/src/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Base class for every error the SDK throws on its own behalf. Errors that
* originate in `@basou/core` (e.g. a malformed `session.yaml`) propagate as-is;
* only the SDK's own preconditions are wrapped, so `instanceof BasouSdkError`
* identifies "the SDK rejected this call" rather than "the data was bad".
*/
export class BasouSdkError extends Error {
constructor(message: string, options?: { cause?: unknown }) {
super(message, options);
this.name = new.target.name;
}
}

/**
* `openWorkspace` was pointed at a path that is not a usable Basou workspace:
* the `.basou/` directory is missing, is a symlink, or is otherwise not a
* directory. The offending repository root is on {@link root}.
*/
export class WorkspaceNotFoundError extends BasouSdkError {
readonly root: string;
constructor(root: string, options?: { cause?: unknown }) {
super(
`No Basou workspace at ${root}: expected a '.basou/' directory (run 'basou init' there first).`,
options,
);
this.root = root;
}
}

/**
* A session / task id prefix matched more than one record. The {@link input}
* is the prefix as given; the caller should retry with a longer one. (A prefix
* that matches nothing is NOT an error — the lookup returns `null` instead.)
*/
export class AmbiguousIdError extends BasouSdkError {
readonly input: string;
constructor(input: string, options?: { cause?: unknown }) {
super(`Ambiguous id '${input}': matched more than one record; use a longer prefix.`, options);
this.input = input;
}
}
13 changes: 9 additions & 4 deletions packages/sdk/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { describe, expect, it } from "vitest";
import { BASOU_SDK_VERSION } from "./index.js";
import { BASOU_SDK_VERSION, openWorkspace, resolveWorkspaceRoot } from "./index.js";

describe("@basou/sdk skeleton", () => {
it("exposes BASOU_SDK_VERSION as 0.1.0", () => {
expect(BASOU_SDK_VERSION).toBe("0.1.0");
describe("@basou/sdk surface", () => {
it("exposes BASOU_SDK_VERSION as 0.2.0 (first runtime read API)", () => {
expect(BASOU_SDK_VERSION).toBe("0.2.0");
});

it("exports the workspace entry points", () => {
expect(typeof openWorkspace).toBe("function");
expect(typeof resolveWorkspaceRoot).toBe("function");
});
});
76 changes: 75 additions & 1 deletion packages/sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,75 @@
export const BASOU_SDK_VERSION = "0.1.0";
/**
* `@basou/sdk` — the stable, read-only programmatic API for reading a Basou
* workspace's provenance (`.basou/`). It is a thin, ergonomic facade over
* `@basou/core`'s readers: open a workspace once and query sessions, events,
* tasks, approvals, status, stats, and the rendered handoff / decisions. No
* writers are exposed — third-party tooling can read provenance without any
* risk of mutating it.
*
* @example
* ```ts
* import { openWorkspace, resolveWorkspaceRoot } from "@basou/sdk";
*
* const root = await resolveWorkspaceRoot(process.cwd()); // or pass a known root
* const ws = await openWorkspace(root);
* for (const { session, suspect } of await ws.listSessions()) {
* console.log(session.session.id, session.session.status, suspect);
* }
* const stats = await ws.stats();
* console.log(stats.totals.billableActiveTimeMs);
* ```
*/

/**
* SDK API version, tracking the Basou SDK surface (not the npm package
* version, which moves in lockstep with the monorepo). `0.2.0` is the first
* release with a runtime read API; `0.1.0` was types-only.
*/
export const BASOU_SDK_VERSION = "0.2.0";

// Read types re-exported from @basou/core so consumers can type the values the
// SDK returns without depending on @basou/core directly. These track the
// on-disk provenance schema.
export type {
ActiveTimeBasis,
Approval,
ApprovalStatus,
CommandExecutedEvent,
DayWorkStats,
DecisionRecordedEvent,
Event,
FileChangedEvent,
LoadedApproval,
Manifest,
MeasureAvailability,
NoteAddedEvent,
RiskLevel,
Session,
SessionEndedEvent,
SessionEntry,
SessionMetrics,
SessionSourceKind,
SessionStartedEvent,
SessionStatus,
SessionStatusChangedEvent,
SessionWorkStats,
SourceWorkStats,
StatusCount,
StatusSnapshot,
SuspectReason,
Task,
TaskDocument,
TaskStatus,
TokenTotals,
WorkStatsResult,
WorkStatsTotals,
} from "@basou/core";
export { AmbiguousIdError, BasouSdkError, WorkspaceNotFoundError } from "./errors.js";
export {
openWorkspace,
resolveWorkspaceRoot,
type StatsOptions,
type Workspace,
type WorkspaceDiagnostic,
type WorkspaceOptions,
} from "./workspace.js";
Loading