@pivanov/claude-wire 0.0.4 → 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/README.md +6 -4
- package/dist/client.d.ts +2 -0
- package/dist/client.js +21 -3
- package/dist/cost.d.ts +6 -0
- package/dist/cost.js +27 -9
- package/dist/errors.d.ts +5 -2
- package/dist/errors.js +28 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +7 -0
- package/dist/json.d.ts +35 -0
- package/dist/json.js +43 -0
- package/dist/parser/translator.js +1 -1
- package/dist/pipeline.js +3 -3
- package/dist/session.d.ts +1 -0
- package/dist/session.js +9 -2
- package/dist/stderr.d.ts +10 -0
- package/dist/stderr.js +31 -0
- package/dist/stream.js +1 -1
- package/dist/types/events.d.ts +1 -1
- package/dist/types/options.d.ts +1 -1
- package/dist/types/results.d.ts +6 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,12 +20,14 @@ console.log(result.costUsd); // 0.0084
|
|
|
20
20
|
## Features
|
|
21
21
|
|
|
22
22
|
- **Simple API** - `claude.ask()` returns a typed result, `claude.stream()` yields events
|
|
23
|
+
- **Structured JSON** - `claude.askJson(prompt, schema)` with Standard Schema (Zod/Valibot/ArkType) validation
|
|
23
24
|
- **Tool control** - allow, block, or intercept any tool at runtime
|
|
24
25
|
- **Multi-turn sessions** - persistent process across multiple prompts
|
|
25
|
-
- **Cost tracking** - per-request budgets with auto-abort
|
|
26
|
+
- **Cost tracking** - per-request budgets with auto-abort and projection primitives
|
|
27
|
+
- **Typed errors** - rate-limit, overload, context-length, retry-exhausted as `KnownError` codes
|
|
26
28
|
- **Fully typed** - discriminated union events, full IntelliSense
|
|
27
|
-
- **Resilient** - auto-respawn, transient error detection, AbortSignal
|
|
28
|
-
- **Zero dependencies** -
|
|
29
|
+
- **Resilient** - auto-respawn with backoff, transient error detection, AbortSignal
|
|
30
|
+
- **Zero dependencies** - ~29 kB gzipped
|
|
29
31
|
|
|
30
32
|
## Install
|
|
31
33
|
|
|
@@ -67,7 +69,7 @@ apps/examples/ interactive example runner
|
|
|
67
69
|
|
|
68
70
|
```bash
|
|
69
71
|
bun install
|
|
70
|
-
bun run test #
|
|
72
|
+
bun run test # 217 tests
|
|
71
73
|
bun run typecheck
|
|
72
74
|
bun run lint
|
|
73
75
|
bun run docs:dev # local docs server
|
package/dist/client.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { type IJsonResult, type TSchemaInput } from "./json.js";
|
|
1
2
|
import type { IClaudeSession } from "./session.js";
|
|
2
3
|
import type { IClaudeStream } from "./stream.js";
|
|
3
4
|
import type { IClaudeOptions, ISessionOptions } from "./types/options.js";
|
|
4
5
|
import type { TAskResult } from "./types/results.js";
|
|
5
6
|
export interface IClaudeClient {
|
|
6
7
|
ask: (prompt: string, options?: IClaudeOptions) => Promise<TAskResult>;
|
|
8
|
+
askJson: <T>(prompt: string, schema: TSchemaInput<T>, options?: IClaudeOptions) => Promise<IJsonResult<T>>;
|
|
7
9
|
stream: (prompt: string, options?: IClaudeOptions) => IClaudeStream;
|
|
8
10
|
session: (options?: ISessionOptions) => IClaudeSession;
|
|
9
11
|
create: (defaults: IClaudeOptions) => IClaudeClient;
|
package/dist/client.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { isStandardSchema, parseAndValidate } from "./json.js";
|
|
1
2
|
import { createSession } from "./session.js";
|
|
2
3
|
import { createStream } from "./stream.js";
|
|
3
4
|
const mergeOptions = (defaults, overrides) => {
|
|
4
5
|
const merged = { ...defaults, ...overrides };
|
|
5
|
-
if (overrides && "
|
|
6
|
-
merged.
|
|
6
|
+
if (overrides && "toolHandler" in overrides) {
|
|
7
|
+
merged.toolHandler = overrides.toolHandler ? { ...defaults.toolHandler, ...overrides.toolHandler } : overrides.toolHandler;
|
|
7
8
|
}
|
|
8
9
|
if (overrides && "env" in overrides) {
|
|
9
10
|
merged.env = overrides.env ? { ...defaults.env, ...overrides.env } : overrides.env;
|
|
@@ -16,6 +17,23 @@ export const createClient = (defaults = {}) => {
|
|
|
16
17
|
const stream = createStream(prompt, merged);
|
|
17
18
|
return stream.result();
|
|
18
19
|
};
|
|
20
|
+
const askJson = async (prompt, schema, options) => {
|
|
21
|
+
const merged = mergeOptions(defaults, options);
|
|
22
|
+
// Forward the raw JSON Schema string to the CLI via --json-schema when
|
|
23
|
+
// the caller passes a string. Standard Schema objects are validated
|
|
24
|
+
// SDK-side after the response arrives.
|
|
25
|
+
if (typeof schema === "string") {
|
|
26
|
+
merged.jsonSchema = schema;
|
|
27
|
+
}
|
|
28
|
+
else if (isStandardSchema(schema)) {
|
|
29
|
+
// Extract JSON Schema representation if available for CLI-side
|
|
30
|
+
// constraint. Many Standard Schema libs expose this via toJsonSchema()
|
|
31
|
+
// but it's not part of the protocol. We validate SDK-side regardless.
|
|
32
|
+
}
|
|
33
|
+
const raw = await ask(prompt, merged);
|
|
34
|
+
const data = parseAndValidate(raw.text, schema);
|
|
35
|
+
return { data, raw };
|
|
36
|
+
};
|
|
19
37
|
const stream = (prompt, options) => {
|
|
20
38
|
const merged = mergeOptions(defaults, options);
|
|
21
39
|
return createStream(prompt, merged);
|
|
@@ -28,5 +46,5 @@ export const createClient = (defaults = {}) => {
|
|
|
28
46
|
const merged = mergeOptions(defaults, newDefaults);
|
|
29
47
|
return createClient(merged);
|
|
30
48
|
};
|
|
31
|
-
return { ask, stream, session, create };
|
|
49
|
+
return { ask, askJson, stream, session, create };
|
|
32
50
|
};
|
package/dist/cost.d.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import type { TCostSnapshot } from "./types/results.js";
|
|
2
2
|
import type { TWarn } from "./warnings.js";
|
|
3
|
+
export interface ICostProjection {
|
|
4
|
+
projectedUsd: number;
|
|
5
|
+
}
|
|
3
6
|
export interface ICostTracker {
|
|
4
7
|
update: (totalCostUsd: number, totalInputTokens: number, totalOutputTokens: number) => void;
|
|
5
8
|
snapshot: () => TCostSnapshot;
|
|
6
9
|
checkBudget: () => void;
|
|
7
10
|
reset: () => void;
|
|
11
|
+
turnCount: number;
|
|
12
|
+
averagePerTurn: number;
|
|
13
|
+
project: (remainingTurns: number) => ICostProjection;
|
|
8
14
|
}
|
|
9
15
|
export interface ICostTrackerOptions {
|
|
10
16
|
maxCostUsd?: number;
|
package/dist/cost.js
CHANGED
|
@@ -5,17 +5,18 @@ export const createCostTracker = (options = {}) => {
|
|
|
5
5
|
assertPositiveNumber(options.maxCostUsd, "maxCostUsd");
|
|
6
6
|
const warn = createWarn(options.onWarning);
|
|
7
7
|
let totalUsd = 0;
|
|
8
|
-
let
|
|
9
|
-
let
|
|
8
|
+
let input = 0;
|
|
9
|
+
let output = 0;
|
|
10
|
+
let turns = 0;
|
|
10
11
|
const snapshot = () => ({
|
|
11
12
|
totalUsd,
|
|
12
|
-
|
|
13
|
-
outputTokens,
|
|
13
|
+
tokens: { input, output },
|
|
14
14
|
});
|
|
15
15
|
const update = (totalCostUsd, totalInputToks, totalOutputToks) => {
|
|
16
16
|
totalUsd = totalCostUsd;
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
input = totalInputToks;
|
|
18
|
+
output = totalOutputToks;
|
|
19
|
+
turns++;
|
|
19
20
|
if (options.onCostUpdate) {
|
|
20
21
|
try {
|
|
21
22
|
options.onCostUpdate(snapshot());
|
|
@@ -32,8 +33,25 @@ export const createCostTracker = (options = {}) => {
|
|
|
32
33
|
};
|
|
33
34
|
const reset = () => {
|
|
34
35
|
totalUsd = 0;
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
input = 0;
|
|
37
|
+
output = 0;
|
|
38
|
+
turns = 0;
|
|
39
|
+
};
|
|
40
|
+
const project = (remainingTurns) => {
|
|
41
|
+
const avg = turns > 0 ? totalUsd / turns : 0;
|
|
42
|
+
return { projectedUsd: totalUsd + avg * remainingTurns };
|
|
43
|
+
};
|
|
44
|
+
return {
|
|
45
|
+
update,
|
|
46
|
+
snapshot,
|
|
47
|
+
checkBudget,
|
|
48
|
+
reset,
|
|
49
|
+
get turnCount() {
|
|
50
|
+
return turns;
|
|
51
|
+
},
|
|
52
|
+
get averagePerTurn() {
|
|
53
|
+
return turns > 0 ? totalUsd / turns : 0;
|
|
54
|
+
},
|
|
55
|
+
project,
|
|
37
56
|
};
|
|
38
|
-
return { update, snapshot, checkBudget, reset };
|
|
39
57
|
};
|
package/dist/errors.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export declare class ProcessError extends ClaudeError {
|
|
|
16
16
|
readonly exitCode?: number | undefined;
|
|
17
17
|
constructor(message: string, exitCode?: number | undefined);
|
|
18
18
|
}
|
|
19
|
-
export declare const KNOWN_ERROR_CODES: readonly ["not-authenticated", "binary-not-found", "permission-denied", "retry-exhausted"];
|
|
19
|
+
export declare const KNOWN_ERROR_CODES: readonly ["not-authenticated", "binary-not-found", "permission-denied", "retry-exhausted", "rate-limit", "overloaded", "context-length-exceeded", "invalid-json-schema", "mcp-error"];
|
|
20
20
|
export type TKnownErrorCode = (typeof KNOWN_ERROR_CODES)[number];
|
|
21
21
|
export declare class KnownError extends ClaudeError {
|
|
22
22
|
readonly code: TKnownErrorCode;
|
|
@@ -25,4 +25,7 @@ export declare class KnownError extends ClaudeError {
|
|
|
25
25
|
export declare const isKnownError: (error: unknown) => error is KnownError;
|
|
26
26
|
export declare const isTransientError: (error: unknown) => boolean;
|
|
27
27
|
export declare const errorMessage: (error: unknown) => string;
|
|
28
|
-
|
|
28
|
+
declare let stderrClassifier: ((stderr: string, exitCode?: number) => TKnownErrorCode | undefined) | undefined;
|
|
29
|
+
export declare const setStderrClassifier: (fn: typeof stderrClassifier) => void;
|
|
30
|
+
export declare const processExitedEarly: (stderr: string, exitCode?: number) => ProcessError | KnownError;
|
|
31
|
+
export {};
|
package/dist/errors.js
CHANGED
|
@@ -37,7 +37,18 @@ export class ProcessError extends ClaudeError {
|
|
|
37
37
|
// Only codes the SDK actually constructs are listed. Add a new code here
|
|
38
38
|
// alongside the throw site that needs it -- aspirational entries give
|
|
39
39
|
// consumers false confidence that they can pattern-match on them.
|
|
40
|
-
export const KNOWN_ERROR_CODES = [
|
|
40
|
+
export const KNOWN_ERROR_CODES = [
|
|
41
|
+
"not-authenticated",
|
|
42
|
+
"binary-not-found",
|
|
43
|
+
"permission-denied",
|
|
44
|
+
"retry-exhausted",
|
|
45
|
+
// Classified from stderr by classifyStderr (src/stderr.ts):
|
|
46
|
+
"rate-limit",
|
|
47
|
+
"overloaded",
|
|
48
|
+
"context-length-exceeded",
|
|
49
|
+
"invalid-json-schema",
|
|
50
|
+
"mcp-error",
|
|
51
|
+
];
|
|
41
52
|
export class KnownError extends ClaudeError {
|
|
42
53
|
code;
|
|
43
54
|
constructor(code, message) {
|
|
@@ -74,4 +85,19 @@ export const errorMessage = (error) => {
|
|
|
74
85
|
// case. session.ts + stream.ts both need this; the string used to be
|
|
75
86
|
// duplicated verbatim, which drifted at least once. Prefix stderr when
|
|
76
87
|
// available because CLI error output is the most actionable signal.
|
|
77
|
-
|
|
88
|
+
// Auto-promotes to KnownError when stderr matches a classifiable pattern.
|
|
89
|
+
// The classifier is injected to avoid a circular import (stderr.ts imports
|
|
90
|
+
// types from this file). Wire it at boot via `setStderrClassifier`.
|
|
91
|
+
let stderrClassifier;
|
|
92
|
+
export const setStderrClassifier = (fn) => {
|
|
93
|
+
stderrClassifier = fn;
|
|
94
|
+
};
|
|
95
|
+
export const processExitedEarly = (stderr, exitCode) => {
|
|
96
|
+
if (stderr && stderrClassifier) {
|
|
97
|
+
const code = stderrClassifier(stderr, exitCode);
|
|
98
|
+
if (code) {
|
|
99
|
+
return new KnownError(code, stderr);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return new ProcessError(stderr || "Process exited without completing the turn", exitCode);
|
|
103
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export type { IClaudeClient } from "./client.js";
|
|
2
2
|
export { createClient } from "./client.js";
|
|
3
3
|
export { BINARY, LIMITS, TIMEOUTS } from "./constants.js";
|
|
4
|
-
export type { ICostTracker, ICostTrackerOptions } from "./cost.js";
|
|
4
|
+
export type { ICostProjection, ICostTracker, ICostTrackerOptions } from "./cost.js";
|
|
5
5
|
export { createCostTracker } from "./cost.js";
|
|
6
6
|
export type { TKnownErrorCode } from "./errors.js";
|
|
7
7
|
export { AbortError, BudgetExceededError, ClaudeError, errorMessage, isKnownError, isTransientError, KNOWN_ERROR_CODES, KnownError, ProcessError, TimeoutError, } from "./errors.js";
|
|
8
|
+
export type { IJsonResult, IStandardSchema, TSchemaInput } from "./json.js";
|
|
9
|
+
export { JsonValidationError, parseAndValidate, stripFences } from "./json.js";
|
|
8
10
|
export { blockFingerprint, extractContent, parseDoubleEncoded } from "./parser/content.js";
|
|
9
11
|
export { parseLine } from "./parser/ndjson.js";
|
|
10
12
|
export type { ITranslator } from "./parser/translator.js";
|
|
@@ -15,6 +17,7 @@ export type { IReaderOptions } from "./reader.js";
|
|
|
15
17
|
export { readNdjsonEvents } from "./reader.js";
|
|
16
18
|
export type { IClaudeSession } from "./session.js";
|
|
17
19
|
export { createSession } from "./session.js";
|
|
20
|
+
export { classifyStderr } from "./stderr.js";
|
|
18
21
|
export type { IClaudeStream } from "./stream.js";
|
|
19
22
|
export { createStream } from "./stream.js";
|
|
20
23
|
export type { IToolHandlerInstance, TToolDecision } from "./tools/handler.js";
|
|
@@ -24,6 +27,6 @@ export { BUILT_IN_TOOL_NAMES, BUILT_IN_TOOLS, isBuiltInTool } from "./tools/regi
|
|
|
24
27
|
export type { TErrorEvent, TRelayEvent, TSessionMetaEvent, TTextEvent, TThinkingEvent, TToolResultEvent, TToolUseEvent, TTurnCompleteEvent, } from "./types/events.js";
|
|
25
28
|
export type { IAskOptions, IClaudeOptions, ISessionOptions, IToolHandler } from "./types/options.js";
|
|
26
29
|
export type { TClaudeContent, TClaudeContentType, TClaudeEvent, TClaudeEventType, TClaudeMessage, TModelUsageEntry } from "./types/protocol.js";
|
|
27
|
-
export type { TAskResult, TCostSnapshot } from "./types/results.js";
|
|
30
|
+
export type { TAskResult, TCostSnapshot, TTokens } from "./types/results.js";
|
|
28
31
|
export { writer } from "./writer.js";
|
|
29
32
|
export declare const claude: import("./client.js").IClaudeClient;
|
package/dist/index.js
CHANGED
|
@@ -3,14 +3,21 @@ export { createClient } from "./client.js";
|
|
|
3
3
|
export { BINARY, LIMITS, TIMEOUTS } from "./constants.js";
|
|
4
4
|
export { createCostTracker } from "./cost.js";
|
|
5
5
|
export { AbortError, BudgetExceededError, ClaudeError, errorMessage, isKnownError, isTransientError, KNOWN_ERROR_CODES, KnownError, ProcessError, TimeoutError, } from "./errors.js";
|
|
6
|
+
export { JsonValidationError, parseAndValidate, stripFences } from "./json.js";
|
|
6
7
|
export { blockFingerprint, extractContent, parseDoubleEncoded } from "./parser/content.js";
|
|
7
8
|
export { parseLine } from "./parser/ndjson.js";
|
|
8
9
|
export { createTranslator } from "./parser/translator.js";
|
|
9
10
|
export { buildArgs, resetResolvedEnvCache, spawnClaude } from "./process.js";
|
|
10
11
|
export { readNdjsonEvents } from "./reader.js";
|
|
11
12
|
export { createSession } from "./session.js";
|
|
13
|
+
export { classifyStderr } from "./stderr.js";
|
|
12
14
|
export { createStream } from "./stream.js";
|
|
13
15
|
export { createToolHandler } from "./tools/handler.js";
|
|
14
16
|
export { BUILT_IN_TOOL_NAMES, BUILT_IN_TOOLS, isBuiltInTool } from "./tools/registry.js";
|
|
15
17
|
export { writer } from "./writer.js";
|
|
18
|
+
// Wire the stderr classifier into the error factory at module load so
|
|
19
|
+
// processExitedEarly can auto-promote ProcessError -> KnownError.
|
|
20
|
+
import { setStderrClassifier } from "./errors.js";
|
|
21
|
+
import { classifyStderr as _classifyStderr } from "./stderr.js";
|
|
22
|
+
setStderrClassifier(_classifyStderr);
|
|
16
23
|
export const claude = createClient();
|
package/dist/json.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ClaudeError } from "./errors.js";
|
|
2
|
+
export interface IStandardSchema<T = unknown> {
|
|
3
|
+
"~standard": {
|
|
4
|
+
version: 1;
|
|
5
|
+
vendor: string;
|
|
6
|
+
validate: (value: unknown) => IStandardResult<T>;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
interface IStandardResult<T> {
|
|
10
|
+
value?: T;
|
|
11
|
+
issues?: ReadonlyArray<{
|
|
12
|
+
message?: string;
|
|
13
|
+
path?: ReadonlyArray<string | number>;
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
16
|
+
export declare class JsonValidationError extends ClaudeError {
|
|
17
|
+
readonly rawText: string;
|
|
18
|
+
readonly issues: ReadonlyArray<{
|
|
19
|
+
message?: string;
|
|
20
|
+
path?: ReadonlyArray<string | number>;
|
|
21
|
+
}>;
|
|
22
|
+
constructor(message: string, rawText: string, issues: ReadonlyArray<{
|
|
23
|
+
message?: string;
|
|
24
|
+
path?: ReadonlyArray<string | number>;
|
|
25
|
+
}>);
|
|
26
|
+
}
|
|
27
|
+
export interface IJsonResult<T> {
|
|
28
|
+
data: T;
|
|
29
|
+
raw: import("./types/results.js").TAskResult;
|
|
30
|
+
}
|
|
31
|
+
export declare const stripFences: (text: string) => string;
|
|
32
|
+
export type TSchemaInput<T> = IStandardSchema<T> | string;
|
|
33
|
+
export declare const isStandardSchema: <T>(schema: TSchemaInput<T>) => schema is IStandardSchema<T>;
|
|
34
|
+
export declare const parseAndValidate: <T>(text: string, schema: TSchemaInput<T>) => T;
|
|
35
|
+
export {};
|
package/dist/json.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ClaudeError } from "./errors.js";
|
|
2
|
+
export class JsonValidationError extends ClaudeError {
|
|
3
|
+
rawText;
|
|
4
|
+
issues;
|
|
5
|
+
constructor(message, rawText, issues) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.rawText = rawText;
|
|
8
|
+
this.issues = issues;
|
|
9
|
+
this.name = "JsonValidationError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
// Strip common fences: ```json ... ```, ``` ... ```, or bare JSON.
|
|
13
|
+
const FENCE_RE = /^\s*```(?:json)?\s*\n?([\s\S]*?)\n?\s*```\s*$/;
|
|
14
|
+
export const stripFences = (text) => {
|
|
15
|
+
const match = text.match(FENCE_RE);
|
|
16
|
+
return match?.[1] ?? text.trim();
|
|
17
|
+
};
|
|
18
|
+
export const isStandardSchema = (schema) => {
|
|
19
|
+
return typeof schema === "object" && schema !== null && "~standard" in schema;
|
|
20
|
+
};
|
|
21
|
+
export const parseAndValidate = (text, schema) => {
|
|
22
|
+
const stripped = stripFences(text);
|
|
23
|
+
let parsed;
|
|
24
|
+
try {
|
|
25
|
+
parsed = JSON.parse(stripped);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
throw new JsonValidationError(`Failed to parse JSON from Claude response: ${err instanceof Error ? err.message : String(err)}`, text, [
|
|
29
|
+
{ message: "Invalid JSON" },
|
|
30
|
+
]);
|
|
31
|
+
}
|
|
32
|
+
if (isStandardSchema(schema)) {
|
|
33
|
+
const result = schema["~standard"].validate(parsed);
|
|
34
|
+
if (result.issues && result.issues.length > 0) {
|
|
35
|
+
const summary = result.issues.map((i) => i.message ?? "validation error").join("; ");
|
|
36
|
+
throw new JsonValidationError(`Schema validation failed: ${summary}`, text, result.issues);
|
|
37
|
+
}
|
|
38
|
+
return result.value;
|
|
39
|
+
}
|
|
40
|
+
// Raw JSON Schema string -- no runtime validation, just parse. The CLI's
|
|
41
|
+
// --json-schema constrains the model output, so we trust it here.
|
|
42
|
+
return parsed;
|
|
43
|
+
};
|
|
@@ -55,7 +55,7 @@ const translateContentBlock = (block) => {
|
|
|
55
55
|
type: "tool_use",
|
|
56
56
|
toolUseId: block.id,
|
|
57
57
|
toolName: block.name,
|
|
58
|
-
input:
|
|
58
|
+
input: block.input ?? {},
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
case "tool_result": {
|
package/dist/pipeline.js
CHANGED
|
@@ -43,8 +43,8 @@ export const dispatchToolDecision = async (proc, toolHandler, event, onWarning)
|
|
|
43
43
|
// wants to carry forward what previous processes already spent -- stream
|
|
44
44
|
// has no such concept and passes it undefined.
|
|
45
45
|
export const applyTurnComplete = (event, costTracker, offsets) => {
|
|
46
|
-
const base = offsets ?? { totalUsd: 0,
|
|
47
|
-
costTracker.update(base.totalUsd + (event.costUsd ?? 0), base.
|
|
46
|
+
const base = offsets ?? { totalUsd: 0, tokens: { input: 0, output: 0 } };
|
|
47
|
+
costTracker.update(base.totalUsd + (event.costUsd ?? 0), base.tokens.input + (event.inputTokens ?? 0), base.tokens.output + (event.outputTokens ?? 0));
|
|
48
48
|
costTracker.checkBudget();
|
|
49
49
|
};
|
|
50
50
|
export const extractText = (events) => {
|
|
@@ -59,7 +59,7 @@ export const buildResult = (events, costTracker, sessionId) => {
|
|
|
59
59
|
return {
|
|
60
60
|
text: extractText(events),
|
|
61
61
|
costUsd: snap.totalUsd,
|
|
62
|
-
tokens:
|
|
62
|
+
tokens: snap.tokens,
|
|
63
63
|
duration: tc?.durationMs ?? 0,
|
|
64
64
|
sessionId,
|
|
65
65
|
events,
|
package/dist/session.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { IAskOptions, ISessionOptions } from "./types/options.js";
|
|
|
2
2
|
import type { TAskResult } from "./types/results.js";
|
|
3
3
|
export interface IClaudeSession extends AsyncDisposable {
|
|
4
4
|
ask: (prompt: string, options?: IAskOptions) => Promise<TAskResult>;
|
|
5
|
+
askJson: <T>(prompt: string, schema: import("./json.js").TSchemaInput<T>, options?: IAskOptions) => Promise<import("./json.js").IJsonResult<T>>;
|
|
5
6
|
close: () => Promise<void>;
|
|
6
7
|
sessionId: string | undefined;
|
|
7
8
|
}
|
package/dist/session.js
CHANGED
|
@@ -2,6 +2,7 @@ import { withTimeout } from "./async.js";
|
|
|
2
2
|
import { LIMITS, MAX_BACKOFF_INDEX, RESPAWN_BACKOFF_MS, TIMEOUTS } from "./constants.js";
|
|
3
3
|
import { createCostTracker } from "./cost.js";
|
|
4
4
|
import { AbortError, BudgetExceededError, ClaudeError, isTransientError, KnownError, processExitedEarly, TimeoutError } from "./errors.js";
|
|
5
|
+
import { parseAndValidate } from "./json.js";
|
|
5
6
|
import { createTranslator } from "./parser/translator.js";
|
|
6
7
|
import { applyTurnComplete, buildResult, startPipeline } from "./pipeline.js";
|
|
7
8
|
import { safeKill, safeWrite } from "./process.js";
|
|
@@ -90,14 +91,14 @@ export const createSession = (options = {}) => {
|
|
|
90
91
|
let currentSessionId;
|
|
91
92
|
let consecutiveCrashes = 0;
|
|
92
93
|
let turnCount = 0;
|
|
93
|
-
let costOffsets = { totalUsd: 0,
|
|
94
|
+
let costOffsets = { totalUsd: 0, tokens: { input: 0, output: 0 } };
|
|
94
95
|
const translator = createTranslator();
|
|
95
96
|
const costTracker = createCostTracker({
|
|
96
97
|
maxCostUsd: options.maxCostUsd,
|
|
97
98
|
onCostUpdate: options.onCostUpdate,
|
|
98
99
|
onWarning: options.onWarning,
|
|
99
100
|
});
|
|
100
|
-
const toolHandler = options.
|
|
101
|
+
const toolHandler = options.toolHandler ? createToolHandler(options.toolHandler) : undefined;
|
|
101
102
|
let inFlight;
|
|
102
103
|
let reader;
|
|
103
104
|
const cleanupProcess = () => {
|
|
@@ -300,8 +301,14 @@ export const createSession = (options = {}) => {
|
|
|
300
301
|
}
|
|
301
302
|
cleanupProcess();
|
|
302
303
|
};
|
|
304
|
+
const askJson = async (prompt, schema, askOpts) => {
|
|
305
|
+
const raw = await ask(prompt, askOpts);
|
|
306
|
+
const data = parseAndValidate(raw.text, schema);
|
|
307
|
+
return { data, raw };
|
|
308
|
+
};
|
|
303
309
|
return {
|
|
304
310
|
ask,
|
|
311
|
+
askJson,
|
|
305
312
|
close,
|
|
306
313
|
get sessionId() {
|
|
307
314
|
return currentSessionId;
|
package/dist/stderr.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TKnownErrorCode } from "./errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* Attempts to classify an opaque stderr string into a typed `TKnownErrorCode`.
|
|
4
|
+
* Returns `undefined` when no pattern matches -- the caller should keep the
|
|
5
|
+
* original `ProcessError` as-is in that case.
|
|
6
|
+
*
|
|
7
|
+
* The exit code is accepted but not currently used (all classification is
|
|
8
|
+
* text-based). Reserved for future exit-code-specific rules.
|
|
9
|
+
*/
|
|
10
|
+
export declare const classifyStderr: (stderr: string, _exitCode?: number) => TKnownErrorCode | undefined;
|
package/dist/stderr.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Conservative pattern table for classifying CLI stderr into typed error
|
|
2
|
+
// codes. Intentionally small -- false classification is worse than no
|
|
3
|
+
// classification. Documented as "may drift across CLI versions."
|
|
4
|
+
//
|
|
5
|
+
// Each entry: [pattern to test against stderr text, resulting code].
|
|
6
|
+
const STDERR_PATTERNS = [
|
|
7
|
+
[/rate[_ -]?limit|429|too many requests/i, "rate-limit"],
|
|
8
|
+
[/overloaded|529|temporarily unavailable/i, "overloaded"],
|
|
9
|
+
[/context[_ -]?length|context[_ -]?window|too long|maximum.*tokens/i, "context-length-exceeded"],
|
|
10
|
+
[/invalid.*json[_ -]?schema|schema.*invalid|json.*schema.*error/i, "invalid-json-schema"],
|
|
11
|
+
[/mcp.*error|mcp.*fail|mcp.*server/i, "mcp-error"],
|
|
12
|
+
[/not authenticated|authentication|unauthorized|401/i, "not-authenticated"],
|
|
13
|
+
[/permission denied|forbidden|403/i, "permission-denied"],
|
|
14
|
+
[/binary.*not found|command not found|ENOENT.*claude/i, "binary-not-found"],
|
|
15
|
+
];
|
|
16
|
+
/**
|
|
17
|
+
* Attempts to classify an opaque stderr string into a typed `TKnownErrorCode`.
|
|
18
|
+
* Returns `undefined` when no pattern matches -- the caller should keep the
|
|
19
|
+
* original `ProcessError` as-is in that case.
|
|
20
|
+
*
|
|
21
|
+
* The exit code is accepted but not currently used (all classification is
|
|
22
|
+
* text-based). Reserved for future exit-code-specific rules.
|
|
23
|
+
*/
|
|
24
|
+
export const classifyStderr = (stderr, _exitCode) => {
|
|
25
|
+
for (const [pattern, code] of STDERR_PATTERNS) {
|
|
26
|
+
if (pattern.test(stderr)) {
|
|
27
|
+
return code;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
};
|
package/dist/stream.js
CHANGED
|
@@ -15,7 +15,7 @@ export const createStream = (prompt, options = {}) => {
|
|
|
15
15
|
// capture config. A pre-aborted signal surfaces on the first access
|
|
16
16
|
// (iterate / text / cost / result), which is when spawn would happen.
|
|
17
17
|
const translator = createTranslator();
|
|
18
|
-
const toolHandler = options.
|
|
18
|
+
const toolHandler = options.toolHandler ? createToolHandler(options.toolHandler) : undefined;
|
|
19
19
|
const costTracker = createCostTracker({
|
|
20
20
|
maxCostUsd: options.maxCostUsd,
|
|
21
21
|
onCostUpdate: options.onCostUpdate,
|
package/dist/types/events.d.ts
CHANGED
package/dist/types/options.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export interface IClaudeOptions {
|
|
|
16
16
|
appendSystemPrompt?: string;
|
|
17
17
|
allowedTools?: TToolName[];
|
|
18
18
|
disallowedTools?: TToolName[];
|
|
19
|
-
|
|
19
|
+
toolHandler?: IToolHandler;
|
|
20
20
|
/**
|
|
21
21
|
* SDK-side budget limit, evaluated after each turn. Throws `BudgetExceededError`
|
|
22
22
|
* and kills the process when `total_cost_usd` exceeds this value. `0` means
|
package/dist/types/results.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import type { TRelayEvent } from "./events.js";
|
|
2
|
+
export type TTokens = {
|
|
3
|
+
input: number;
|
|
4
|
+
output: number;
|
|
5
|
+
};
|
|
2
6
|
export type TCostSnapshot = {
|
|
3
7
|
totalUsd: number;
|
|
4
|
-
|
|
5
|
-
outputTokens: number;
|
|
8
|
+
tokens: TTokens;
|
|
6
9
|
};
|
|
7
10
|
export type TAskResult = {
|
|
8
11
|
text: string;
|
|
9
12
|
costUsd: number;
|
|
10
|
-
tokens:
|
|
11
|
-
input: number;
|
|
12
|
-
output: number;
|
|
13
|
-
};
|
|
13
|
+
tokens: TTokens;
|
|
14
14
|
duration: number;
|
|
15
15
|
sessionId?: string;
|
|
16
16
|
events: TRelayEvent[];
|