Skip to content

@polydeukes/sdk-ts

Three Promise-returning verbs run a separately installed pdks executable. The SDK sends the caller’s input and returns a judgment or memory result as a value. The CLI owns judgment, configuration, and telemetry.

Verb Command Result
checkCovenant pdks covenant check --enforce <level> --unattended CheckCovenantVerdict
checkChangeSet pdks covenant check --diff --enforce <level> CheckCovenantVerdict
memorySearch pdks memory search <query> --json MemorySearchOutcome

All three verbs share executable resolution and byte-oriented process communication. Roots become absolute relative to the caller’s current directory and become the child’s cwd. An explicit executable is also resolved relative to the caller’s cwd, even when it is a bare filename; symlink spelling is preserved. Otherwise, the first executable regular file named pdks in an absolute PATH directory is selected. Empty and relative entries are skipped. A selected executable’s failure returns a failure value without retrying another candidate.

Terminal window
pnpm add @polydeukes/sdk-ts

The SDK host needs Node >=24. Install pdks for Linux x64, Linux arm64, or macOS arm64 using the installation guide. For a Release download, extract the .tar.gz archive first, then select the extracted executable through executable or an absolute PATH directory. The SDK runs that file directly. Runtime and peer dependencies on @polydeukes/core and polydeukes are unnecessary; transport types ship in the SDK.

Memory search runs pdks memory ..., which invokes pdks-memory on the SDK process’s PATH. Follow the installation guide to install the extension. For a local npm installation, add the project’s absolute node_modules/.bin directory to PATH before starting the SDK host. If it is missing, search returns unavailable with the reason pdks prints.

import { checkCovenant, type CovenantInput } from '@polydeukes/sdk-ts';
const input: CovenantInput = {
toolCalls: [
{
name: 'writeFile',
args: { path: 'src/index.ts' },
fileChange: {
kind: 'create',
path: 'src/index.ts',
post: 'export const answer = 42;\n',
},
},
],
subagentSpawns: [],
userMessages: [],
tools: { mutating: ['writeFile'], shell: ['exec'], commandArgs: ['command'] },
};
const verdict = await checkCovenant({
repoRoot: '/path/to/project',
input,
executable: '/opt/tools/pdks',
timeoutMs: 2000,
});
if (verdict.verdict !== 'upheld') throw new Error(verdict.reason);
// Perform the proposed action here, after an upheld verdict.

The host supplies evidence for each tool call. fileChange is create (path, post), modify (path, pre, post), or delete (path, optional pre). An absent fileChange means that call has no proven file mutation; another call’s evidence does not fill it in. The SDK sends the input unchanged as UTF-8 JSON and synthesizes no actor, session, or tool roster. CovenantInput has no world field: the CLI supplies that axis from the project.

checkCovenant runs covenant check --enforce <level> --unattended, followed by --config-layer and --telemetry-path when given. There is no option to turn off --unattended. A token in session.userMessages does not open the witness valve, and disciplines whose bodies read userTexts record skipped with reason no-observation for each call inside their scope. The SDK requires pdks from the same release or later; an older pdks rejects --unattended with its usage line and exit 2, which the SDK returns as a blocked verdict.

Field CheckCovenantSpec CheckChangeSetSpec
repoRoot: string Project root and child cwd Same
input: CovenantInput Required caller-built IR Replaced by diff: string
enforce?: 'advise' | 'block' Defaults to block Same
configLayer?: string Passed as --config-layer; relative to child cwd Same
telemetryPath?: string Passed as --telemetry-path; relative to child cwd Same
executable?: string Explicit executable, ahead of PATH Same
timeoutMs?: number Finite nonnegative milliseconds; absent means no deadline Same
spawn?: Spawn Optional process boundary Same

MemorySearchSpec takes root: string, executable, timeoutMs, spawn, query: string and optional limit: number. The limit must be a positive safe integer. Invalid limits and deadlines return failure values before spawning. Memory commands receive no enforcement, config-layer, or telemetry flags.

The communication deadline starts after the process is spawned. It includes writing stdin and collecting both output pipes, including pipes held by descendants after the direct child exits. Zero expires immediately after spawn. Expiry kills and reaps the direct child and closes the pipes; it does not kill descendants. Resolution, serialization, and OS spawn delay precede the deadline. Injected spawns enforce the deadline themselves.

type CheckCovenantVerdict =
| { verdict: 'upheld'; advisories: string }
| { verdict: 'blocked'; reason: string }
| { verdict: 'unjudged'; reason: string };

Status 0 means upheld, 2 means blocked, and every other status or signal means unjudged. Resolution, serialization, spawn, and deadline failures also return unjudged. Only upheld authorizes the proposed action. The SDK does not reinterpret status 2 using stderr. Cycles, BigInt, and non-finite JSON numbers fail serialization.

Valid stderr text is preserved, including whitespace; malformed UTF-8 uses replacement decoding. advisories and a blocked response’s reason are the child’s stderr. Other completed nonzero statuses use stderr when present, with a status explanation when absent. Covenant stdout is drained but does not determine the verdict. Telemetry rows are written by the CLI; pass telemetryPath to select their destination.

import { checkChangeSet } from '@polydeukes/sdk-ts';
const verdict = await checkChangeSet({
repoRoot: '/path/to/project',
diff,
executable: '/opt/tools/pdks',
});
if (verdict.verdict !== 'upheld') throw new Error(verdict.reason);

The diff goes to stdin as UTF-8 with its newlines preserved. Its a/ and b/ paths are relative to repoRoot. The SDK does not run git. For a stable producer format use:

Terminal window
git diff --no-color --no-ext-diff --src-prefix=a/ --dst-prefix=b/ HEAD

Untracked files need git add -N before they appear. SDK enforcement defaults to block for both covenant verbs, including the change-set command.

import { memorySearch } from '@polydeukes/sdk-ts';
const shared = { root: '/path/to/project', executable: '/opt/tools/pdks', timeoutMs: 2000 };
const result = await memorySearch({ ...shared, query: 'session report', limit: 5 });
switch (result.outcome) {
case 'found':
console.log(result.hits);
break;
case 'empty':
console.log('No matching sections');
break;
case 'unavailable':
throw new Error(result.reason);
}

MemorySearchOutcome is found with a nonempty hits list, empty, or unavailable with a reason. Search passes the query as one literal argument and limit as --limit <n>. Memory stdin is empty.

The search output follows memory-search-output.schema.json in the repository’s schema/ directory. Successful search stdout must be strict UTF-8 JSON with a results array ({"results": [...]}). Each result requires id, conceptId, docTitle, sectionTitle, status (strings), stale (boolean), supersededBy (string array), trust (human-reviewed, machine-verified, or unverified), and matchPath (and, or, or like). Additional object fields are allowed. MemorySearchResult is generated from the repository’s memory output schema. Ranking order is preserved. Only a valid empty results array means empty; invalid responses and execution failures mean unavailable.

An unavailable executable returns unjudged for covenant commands and unavailable for memory. Handle those branches explicitly, as the examples above do.

Consumers that previously installed npm peers should install a standalone pdks and select it through executable or an absolute PATH directory. Import input types from @polydeukes/sdk-ts, replacing imports from @polydeukes/core. The old CheckCovenantSpawnSpec name becomes SpawnSpec. The process boundary now handles three verbs and carries binary stdin, stdout, and stderr:

type SpawnSpec = {
command: string;
args: string[];
cwd: string;
stdin: Uint8Array;
timeoutMs?: number;
};
type SpawnOutcome = { status: number | null; stdout: Uint8Array; stderr: Uint8Array };
type Spawn = (spec: SpawnSpec) => Promise<SpawnOutcome>;

Decode stderr with replacement semantics, but retain stdout bytes until search decoding. An injected Error named TimeoutError maps to the deadline failure branch; ordinary thrown errors also become failure values.

The SDK does not negotiate CLI versions. Use a CLI that understands the input schema and memory enums required by the SDK. Generated TypeScript types describe transport shapes; semantic constraints such as a nonempty shell roster needing command argument names are checked by the CLI. A valid stderr advisory can accompany upheld when the chosen enforcement posture permits an action. Memory results describe retrieval, not authorization.