@vibedgc/sdk 0.6.4
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 +201 -0
- package/README.md +53 -0
- package/dist/audit.d.ts +16 -0
- package/dist/audit.js +179 -0
- package/dist/changes.d.ts +26 -0
- package/dist/changes.js +377 -0
- package/dist/client.d.ts +53 -0
- package/dist/client.js +472 -0
- package/dist/errors.d.ts +53 -0
- package/dist/errors.js +70 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +9 -0
- package/dist/mcp-bridge.mjs +36 -0
- package/dist/policy.d.ts +128 -0
- package/dist/policy.js +795 -0
- package/dist/runtime.d.ts +31 -0
- package/dist/runtime.js +148 -0
- package/dist/schema.d.ts +4 -0
- package/dist/schema.js +124 -0
- package/dist/session.d.ts +264 -0
- package/dist/session.js +1739 -0
- package/dist/state.d.ts +37 -0
- package/dist/state.js +218 -0
- package/dist/tools.d.ts +56 -0
- package/dist/tools.js +339 -0
- package/dist/transport.d.ts +72 -0
- package/dist/transport.js +495 -0
- package/dist/types.d.ts +362 -0
- package/dist/types.js +3 -0
- package/dist/usage.d.ts +50 -0
- package/dist/usage.js +149 -0
- package/package.json +40 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
export declare const VERSION = "0.6.4";
|
|
2
|
+
export declare const PROTOCOL = 14;
|
|
3
|
+
export declare const REQUIRES_CLI = "0.43.4";
|
|
4
|
+
export type PermissionMode = "default" | "acceptEdits" | "plan" | "auto";
|
|
5
|
+
export type PermissionAction = "once" | "always" | "deny";
|
|
6
|
+
export type PlanAction = "auto" | "acceptEdits" | "default" | "reject";
|
|
7
|
+
export type UnhandledPolicy = "deny" | "callback";
|
|
8
|
+
export type SandboxRequirement = "required" | "preferred" | "off";
|
|
9
|
+
/** `sandbox` option: the bare requirement or `{ requirement }`. */
|
|
10
|
+
export type SandboxSetting = SandboxRequirement | {
|
|
11
|
+
requirement?: SandboxRequirement;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Whether a session's shell commands run inside the OS sandbox (`session.sandbox`).
|
|
15
|
+
* `requirement` is what the session asked for (a sandboxed-shell RuntimePolicy asks for at least
|
|
16
|
+
* "preferred"), `active` whether the runtime confines shell commands, `backend` the confinement
|
|
17
|
+
* tool (`bwrap` or `sandbox-exec`), and `reason` why a "preferred" sandbox is off.
|
|
18
|
+
*/
|
|
19
|
+
export type SandboxStatus = {
|
|
20
|
+
requirement: SandboxRequirement;
|
|
21
|
+
active: boolean;
|
|
22
|
+
backend: string;
|
|
23
|
+
reason: string;
|
|
24
|
+
};
|
|
25
|
+
/** A run's state. (0.5.2 listed a "blocked" status that was never produced; see `denials`.) */
|
|
26
|
+
export type RunStatus = "queued" | "running" | "waiting_for_approval" | "completed" | "cancelled" | "failed";
|
|
27
|
+
export type PermissionRequest = {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
args: Record<string, unknown>;
|
|
31
|
+
summary?: string;
|
|
32
|
+
suggestedRule?: string;
|
|
33
|
+
callId?: string;
|
|
34
|
+
diff?: string;
|
|
35
|
+
command?: string;
|
|
36
|
+
};
|
|
37
|
+
export type PlanRequest = {
|
|
38
|
+
id: string;
|
|
39
|
+
plan: string;
|
|
40
|
+
choices: string[];
|
|
41
|
+
};
|
|
42
|
+
export type QuestionRequest = {
|
|
43
|
+
id: string;
|
|
44
|
+
questions: Array<{
|
|
45
|
+
id: string;
|
|
46
|
+
question: string;
|
|
47
|
+
header?: string;
|
|
48
|
+
multiSelect?: boolean;
|
|
49
|
+
options: Array<{
|
|
50
|
+
label: string;
|
|
51
|
+
description?: string;
|
|
52
|
+
recommended?: boolean;
|
|
53
|
+
}>;
|
|
54
|
+
}>;
|
|
55
|
+
callId?: string;
|
|
56
|
+
};
|
|
57
|
+
export type QuestionAnswers = Record<string, {
|
|
58
|
+
selected: number[];
|
|
59
|
+
other?: string;
|
|
60
|
+
}>;
|
|
61
|
+
export type McpInputRequest = {
|
|
62
|
+
id: string;
|
|
63
|
+
server: string;
|
|
64
|
+
kind: string;
|
|
65
|
+
payload: Record<string, unknown>;
|
|
66
|
+
};
|
|
67
|
+
export type McpInputResponse = {
|
|
68
|
+
action: "accept" | "decline" | "cancel";
|
|
69
|
+
content?: Record<string, unknown>;
|
|
70
|
+
};
|
|
71
|
+
export type Goal = {
|
|
72
|
+
text: string;
|
|
73
|
+
status: "none" | "active" | "paused" | "completed" | "blocked";
|
|
74
|
+
elapsedSeconds?: number;
|
|
75
|
+
};
|
|
76
|
+
export type HookInfo = {
|
|
77
|
+
event: string;
|
|
78
|
+
configured: number;
|
|
79
|
+
matchers?: string[];
|
|
80
|
+
valid?: boolean;
|
|
81
|
+
};
|
|
82
|
+
export type Monitor = {
|
|
83
|
+
id: string;
|
|
84
|
+
description?: string;
|
|
85
|
+
command?: string;
|
|
86
|
+
state?: string;
|
|
87
|
+
};
|
|
88
|
+
export type PermissionRule = {
|
|
89
|
+
action: string;
|
|
90
|
+
rule: string;
|
|
91
|
+
};
|
|
92
|
+
export type McpServerInfo = {
|
|
93
|
+
name: string;
|
|
94
|
+
state: string;
|
|
95
|
+
enabled?: boolean;
|
|
96
|
+
toolCount?: number;
|
|
97
|
+
error?: string;
|
|
98
|
+
};
|
|
99
|
+
export type SkillInfo = {
|
|
100
|
+
name: string;
|
|
101
|
+
description: string;
|
|
102
|
+
source: string;
|
|
103
|
+
enabled: boolean;
|
|
104
|
+
};
|
|
105
|
+
export type AgentInfo = {
|
|
106
|
+
id: string;
|
|
107
|
+
state: string;
|
|
108
|
+
description?: string;
|
|
109
|
+
parentId?: string | null;
|
|
110
|
+
};
|
|
111
|
+
export type VerificationResult = {
|
|
112
|
+
ok: boolean | null;
|
|
113
|
+
command: string;
|
|
114
|
+
output?: string;
|
|
115
|
+
exitCode?: number | null;
|
|
116
|
+
};
|
|
117
|
+
export type Artifact = {
|
|
118
|
+
id: string;
|
|
119
|
+
name: string;
|
|
120
|
+
url: string;
|
|
121
|
+
rel?: string;
|
|
122
|
+
};
|
|
123
|
+
export type ToolRecord = {
|
|
124
|
+
name: string;
|
|
125
|
+
callId: string;
|
|
126
|
+
summary?: string;
|
|
127
|
+
output?: string;
|
|
128
|
+
isError?: boolean;
|
|
129
|
+
isDiff?: boolean;
|
|
130
|
+
diff?: string;
|
|
131
|
+
args?: Record<string, unknown>;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* One tool call the run refused (`RunResult.denials`). `source` is a best-effort category of who
|
|
135
|
+
* refused it: "policy" (a RuntimePolicy or a deny rule), "callback" (your onPermission said deny,
|
|
136
|
+
* or none answered), "hook" (a PreToolUse hook), "mode" (plan mode, or a turn with nobody to
|
|
137
|
+
* approve), or "runtime" when it cannot be told apart.
|
|
138
|
+
*/
|
|
139
|
+
export type Denial = {
|
|
140
|
+
name: string;
|
|
141
|
+
reason: string;
|
|
142
|
+
source: "policy" | "callback" | "hook" | "mode" | "runtime";
|
|
143
|
+
callId: string;
|
|
144
|
+
args: Record<string, unknown>;
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* One file the run changed under the session cwd. `before`/`after` hold the text (up to 1 MB; ""
|
|
148
|
+
* for binary files or the missing side) and `diff` is a `git apply`-able unified diff ("" when
|
|
149
|
+
* either side is not text).
|
|
150
|
+
*/
|
|
151
|
+
export type FileChange = {
|
|
152
|
+
path: string;
|
|
153
|
+
kind: "added" | "modified" | "deleted";
|
|
154
|
+
before: string;
|
|
155
|
+
after: string;
|
|
156
|
+
root: string;
|
|
157
|
+
diff: string;
|
|
158
|
+
};
|
|
159
|
+
export type SessionInfo = {
|
|
160
|
+
id: string;
|
|
161
|
+
path: string;
|
|
162
|
+
name?: string;
|
|
163
|
+
preview?: string;
|
|
164
|
+
messageCount?: number;
|
|
165
|
+
when?: string;
|
|
166
|
+
};
|
|
167
|
+
export type Checkpoint = {
|
|
168
|
+
index: number;
|
|
169
|
+
preview: string;
|
|
170
|
+
files: number;
|
|
171
|
+
};
|
|
172
|
+
export type RunEvent = {
|
|
173
|
+
type: string;
|
|
174
|
+
data: Record<string, unknown>;
|
|
175
|
+
runId: string;
|
|
176
|
+
sessionId: string;
|
|
177
|
+
/** The prompt, steer or repair this event belongs to, when DGC said. */
|
|
178
|
+
requestId?: string;
|
|
179
|
+
};
|
|
180
|
+
export type TaskItem = {
|
|
181
|
+
id: string;
|
|
182
|
+
content: string;
|
|
183
|
+
status: "pending" | "in_progress" | "completed" | "blocked" | "cancelled";
|
|
184
|
+
revision?: number;
|
|
185
|
+
};
|
|
186
|
+
export type RunResult = {
|
|
187
|
+
sessionId: string;
|
|
188
|
+
runId: string;
|
|
189
|
+
status: RunStatus;
|
|
190
|
+
reason: string;
|
|
191
|
+
finalText: string;
|
|
192
|
+
partialText?: string;
|
|
193
|
+
output?: unknown;
|
|
194
|
+
/**
|
|
195
|
+
* Provider token usage for this run's turns (`input_tokens`, `output_tokens`,
|
|
196
|
+
* `cached_input_tokens`, `reasoning_tokens`, `requests`; `null` when the provider did not
|
|
197
|
+
* report them, never a guessed 0), `cost_usd` (null without pricing or known tokens),
|
|
198
|
+
* `usage_known`, `model`, `department`, and DGC's `token_estimate` / `context_used` /
|
|
199
|
+
* `context_size` (context-window estimates, not billed tokens).
|
|
200
|
+
*/
|
|
201
|
+
usage: Record<string, unknown>;
|
|
202
|
+
tools: ToolRecord[];
|
|
203
|
+
/** Tool calls the run refused (a denied step does not fail the run; the agent is told). */
|
|
204
|
+
denials?: Denial[];
|
|
205
|
+
artifacts: Artifact[];
|
|
206
|
+
documents: Artifact[];
|
|
207
|
+
/** Files the run added, modified or deleted under the session cwd. */
|
|
208
|
+
changes?: FileChange[];
|
|
209
|
+
tasks?: TaskItem[];
|
|
210
|
+
agents?: AgentInfo[];
|
|
211
|
+
/**
|
|
212
|
+
* Evidence about the configured `verifyCommand` (never about an unrelated command); undefined
|
|
213
|
+
* when none is configured, `ok: null` when nothing shows whether it passed.
|
|
214
|
+
*/
|
|
215
|
+
verification?: VerificationResult;
|
|
216
|
+
error?: string;
|
|
217
|
+
};
|
|
218
|
+
export type SessionOptions = {
|
|
219
|
+
cwd: string;
|
|
220
|
+
permissions?: {
|
|
221
|
+
mode?: PermissionMode;
|
|
222
|
+
unhandled?: UnhandledPolicy;
|
|
223
|
+
};
|
|
224
|
+
onPermission?: (request: PermissionRequest) => PermissionAction | Promise<PermissionAction>;
|
|
225
|
+
onPlan?: (request: PlanRequest) => PlanAction | Promise<PlanAction>;
|
|
226
|
+
onQuestion?: (request: QuestionRequest) => QuestionAnswers | "dismiss" | Promise<QuestionAnswers | "dismiss">;
|
|
227
|
+
onMcpInput?: (request: McpInputRequest) => McpInputResponse | Promise<McpInputResponse>;
|
|
228
|
+
model?: string;
|
|
229
|
+
baseUrl?: string;
|
|
230
|
+
apiKey?: string;
|
|
231
|
+
mode?: PermissionMode;
|
|
232
|
+
thinking?: string;
|
|
233
|
+
instructions?: string;
|
|
234
|
+
/** Cap on tool iterations for every run of this session (this session only). */
|
|
235
|
+
maxTurns?: number;
|
|
236
|
+
/** DGC's per-turn time budget in seconds (this session only). */
|
|
237
|
+
turnBudgetS?: number;
|
|
238
|
+
/** Maximum output tokens per model reply (this session only). */
|
|
239
|
+
maxTokens?: number;
|
|
240
|
+
/**
|
|
241
|
+
* How long a decision callback (onPermission, onPlan, onQuestion, onMcpInput) may take.
|
|
242
|
+
* Default 30 000 ms; `null` waits as long as it takes. A cancel always wins.
|
|
243
|
+
*/
|
|
244
|
+
decisionTimeoutMs?: number | null;
|
|
245
|
+
/** Command DGC runs before it may finish (verify-before-done), for this session only. */
|
|
246
|
+
verifyCommand?: string;
|
|
247
|
+
/** Overrides the client's `sandbox` for this session. */
|
|
248
|
+
sandbox?: SandboxSetting;
|
|
249
|
+
tools?: Array<{
|
|
250
|
+
name: string;
|
|
251
|
+
description: string;
|
|
252
|
+
inputSchema: Record<string, unknown>;
|
|
253
|
+
handler: (args: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
254
|
+
timeoutMs?: number | null;
|
|
255
|
+
}>;
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* Limits an embedder puts on every session of a DGC client. DGC enforces them in every
|
|
259
|
+
* permission mode, `auto` included, on the runtime side (the policy travels to each session's
|
|
260
|
+
* `dgc serve` in its environment and is never saved to any config.json).
|
|
261
|
+
*
|
|
262
|
+
* - Tools: `denyTools` / `allowTools` take DGC tool names (`read_file`, `bash`, `web_fetch`, ...)
|
|
263
|
+
* and MCP routes (`mcp__app__issue_refund` for a `defineTool` tool named `issue_refund`, or
|
|
264
|
+
* `mcp__app__*`). An unknown name throws DGCConfigError. With `allowTools`, every other tool is
|
|
265
|
+
* refused (the option picker `propose_options` stays unless denied).
|
|
266
|
+
* - Paths: DGC's file tools cannot reach outside the session's cwd, except to read inside
|
|
267
|
+
* `extraReadDirs`, and cannot reach `denyPathPrefixes` at all (relative prefixes resolve
|
|
268
|
+
* against the cwd).
|
|
269
|
+
* - Network: `network: "deny"` (the default) refuses web fetch, web search, the browser, skill
|
|
270
|
+
* downloads, and MCP servers other than the application's own tools.
|
|
271
|
+
* - Shell: `shell: "sandboxed"` (default) turns the OS sandbox on for the session; in `auto` mode
|
|
272
|
+
* the shell runs only there (without a sandbox `bash`/`monitor` are refused, `python` always
|
|
273
|
+
* is), and in the other modes each shell command is a permission request for `onPermission`.
|
|
274
|
+
* `shell: "screened"` runs the shell unconfined and, in auto mode, refuses commands whose text
|
|
275
|
+
* looks like a network call or (with a write tool denied) a file write: best effort only.
|
|
276
|
+
* - `redactEvents` (default true) redacts secrets in audit rows.
|
|
277
|
+
*/
|
|
278
|
+
export type RuntimePolicy = {
|
|
279
|
+
network?: "deny" | "allow";
|
|
280
|
+
denyTools?: string[];
|
|
281
|
+
allowTools?: string[] | null;
|
|
282
|
+
extraReadDirs?: string[];
|
|
283
|
+
denyPathPrefixes?: string[];
|
|
284
|
+
redactEvents?: boolean;
|
|
285
|
+
shell?: "sandboxed" | "screened";
|
|
286
|
+
};
|
|
287
|
+
/** USD per 1 000 000 tokens. `cachedInputPerMillion` (0 = the input price) prices cached input. */
|
|
288
|
+
export type Pricing = {
|
|
289
|
+
inputPerMillion?: number;
|
|
290
|
+
outputPerMillion?: number;
|
|
291
|
+
cachedInputPerMillion?: number;
|
|
292
|
+
};
|
|
293
|
+
export type ClientOptions = {
|
|
294
|
+
runtime?: string[];
|
|
295
|
+
/**
|
|
296
|
+
* Holds this client's isolated HOME, audit and usage logs. Must be private (owned by you, not
|
|
297
|
+
* group/world-writable); left unset, a fresh private temporary directory is used. A
|
|
298
|
+
* config.json already in it is never merged: every session writes its own from scratch.
|
|
299
|
+
*/
|
|
300
|
+
stateDir?: string;
|
|
301
|
+
inheritUserState?: boolean;
|
|
302
|
+
model?: string;
|
|
303
|
+
baseUrl?: string;
|
|
304
|
+
apiKey?: string;
|
|
305
|
+
mode?: PermissionMode;
|
|
306
|
+
thinking?: string;
|
|
307
|
+
extraEnv?: Record<string, string>;
|
|
308
|
+
/** Host variables the runtime may see besides the basic ones: false (default), names, or true. */
|
|
309
|
+
inheritEnv?: boolean | string[];
|
|
310
|
+
instructions?: string;
|
|
311
|
+
department?: string;
|
|
312
|
+
pricing?: Pricing;
|
|
313
|
+
policy?: RuntimePolicy;
|
|
314
|
+
/** OS sandbox for shell commands: "required", "preferred" or "off" (default). */
|
|
315
|
+
sandbox?: SandboxSetting;
|
|
316
|
+
/** Extra DGC settings written into every isolated session's config (the explicit way to add them). */
|
|
317
|
+
extraConfig?: Record<string, unknown>;
|
|
318
|
+
/**
|
|
319
|
+
* Whether a session's workspace may grant itself capabilities: its own `.dgc/permissions.json`
|
|
320
|
+
* allow rules and its `.dgc/agents` definitions (which choose a model endpoint and a credential
|
|
321
|
+
* variable). Default false: the workspace can only narrow what runs. Set it when you trust the
|
|
322
|
+
* checkout as your own machine would.
|
|
323
|
+
*/
|
|
324
|
+
trustWorkspace?: boolean;
|
|
325
|
+
/** Keep the temporary stateDir the SDK created (stateDir unset) instead of removing it on close(). */
|
|
326
|
+
keepStateDir?: boolean;
|
|
327
|
+
/** Bound on the runtime's startup handshake (default 30 000 ms). */
|
|
328
|
+
startTimeoutMs?: number;
|
|
329
|
+
/** Bound on each control request such as listSessions or setGoal (default 15 000 ms). */
|
|
330
|
+
requestTimeoutMs?: number;
|
|
331
|
+
};
|
|
332
|
+
/**
|
|
333
|
+
* What `signal` needs from an `AbortSignal` (a standard `AbortController().signal` fits). Declared
|
|
334
|
+
* here so the package's types do not depend on DOM or Node type libraries.
|
|
335
|
+
*/
|
|
336
|
+
export type AbortSignalLike = {
|
|
337
|
+
readonly aborted: boolean;
|
|
338
|
+
addEventListener(type: "abort", listener: () => void, options?: {
|
|
339
|
+
once?: boolean;
|
|
340
|
+
}): void;
|
|
341
|
+
removeEventListener(type: "abort", listener: () => void): void;
|
|
342
|
+
};
|
|
343
|
+
/** Environment variables, as `process.env` holds them. */
|
|
344
|
+
export type Env = Record<string, string | undefined>;
|
|
345
|
+
export type RunOptions = {
|
|
346
|
+
/**
|
|
347
|
+
* Limit for each turn of the run (default 180 000 ms; `null` means no limit). When it passes,
|
|
348
|
+
* the run is cancelled and its result is `status: "failed"`, `reason: "timeout"`.
|
|
349
|
+
*/
|
|
350
|
+
timeoutMs?: number | null;
|
|
351
|
+
/** Aborting this signal cancels the run (result `status: "cancelled"`). */
|
|
352
|
+
signal?: AbortSignalLike;
|
|
353
|
+
maxTurns?: number;
|
|
354
|
+
outputSchema?: Record<string, unknown>;
|
|
355
|
+
repairAttempts?: number;
|
|
356
|
+
skills?: string[];
|
|
357
|
+
workflow?: string;
|
|
358
|
+
};
|
|
359
|
+
export type ResumeOptions = SessionOptions & {
|
|
360
|
+
sessionId?: string;
|
|
361
|
+
latest?: boolean;
|
|
362
|
+
};
|
package/dist/types.js
ADDED
package/dist/usage.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Pricing } from "./types.ts";
|
|
2
|
+
export declare const USAGE_TOTAL_KEYS: readonly ["input_tokens", "output_tokens", "cached_input_tokens", "reasoning_tokens", "requests"];
|
|
3
|
+
export type UsageKey = typeof USAGE_TOTAL_KEYS[number];
|
|
4
|
+
export type UsageTotals = Record<UsageKey, number>;
|
|
5
|
+
export declare function emptyTotals(): UsageTotals;
|
|
6
|
+
export declare function count(value: unknown): number | null;
|
|
7
|
+
/**
|
|
8
|
+
* Cost of one usage record, or null without pricing or when the token counts are unknown.
|
|
9
|
+
* `inputTokens` counts every prompt token, cached ones included (as DGC reports them);
|
|
10
|
+
* `cachedInputTokens` is the part served from a cache, billed at the cached price.
|
|
11
|
+
*/
|
|
12
|
+
export declare function costUsd(inputTokens: number | null, outputTokens: number | null, cachedInputTokens: number | null, pricing: Pricing | undefined): number | null;
|
|
13
|
+
/** The cumulative session totals a `context` event carries, or null if it has none. */
|
|
14
|
+
export declare function usageTotals(event: Record<string, unknown>): UsageTotals | null;
|
|
15
|
+
/** What one turn added, or null when the totals were reset (a new or resumed session). */
|
|
16
|
+
export declare function usageDelta(before: UsageTotals, after: UsageTotals): UsageTotals | null;
|
|
17
|
+
/** False when requests were made but the provider reported no token usage for them. */
|
|
18
|
+
export declare function reported(delta: UsageTotals): boolean;
|
|
19
|
+
export type UsageReport = {
|
|
20
|
+
runs: number;
|
|
21
|
+
/** Runs whose provider did not report tokens; their tokens and cost are left out of the sums. */
|
|
22
|
+
unknownUsageRuns: number;
|
|
23
|
+
inputTokens: number;
|
|
24
|
+
outputTokens: number;
|
|
25
|
+
cachedInputTokens: number;
|
|
26
|
+
/** Sum over runs with a known cost, or null when none has one. */
|
|
27
|
+
costUsd: number | null;
|
|
28
|
+
byDepartment: Array<{
|
|
29
|
+
department: string;
|
|
30
|
+
runs: number;
|
|
31
|
+
unknownUsageRuns: number;
|
|
32
|
+
inputTokens: number;
|
|
33
|
+
outputTokens: number;
|
|
34
|
+
costUsd: number;
|
|
35
|
+
}>;
|
|
36
|
+
/** The last 500 rows as recorded (snake_case keys, the same JSONL the Python SDK writes). */
|
|
37
|
+
rows: Array<Record<string, unknown>>;
|
|
38
|
+
};
|
|
39
|
+
/** Append-only JSONL under the isolated stateDir. Queryable without dgc serve. */
|
|
40
|
+
export declare class UsageLog {
|
|
41
|
+
readonly directory: string;
|
|
42
|
+
readonly path: string;
|
|
43
|
+
constructor(directory: string);
|
|
44
|
+
record(row: Record<string, unknown>): void;
|
|
45
|
+
query(options?: {
|
|
46
|
+
department?: string;
|
|
47
|
+
since?: number;
|
|
48
|
+
until?: number;
|
|
49
|
+
}): UsageReport;
|
|
50
|
+
}
|
package/dist/usage.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-run usage records and cost. Prices are embedder-supplied; DGC does not invent tariffs.
|
|
3
|
+
* Mirrors sdk/python/dgc_sdk/usage.py.
|
|
4
|
+
*
|
|
5
|
+
* Token counts come from the provider usage DGC reports for the session (the `context` event's
|
|
6
|
+
* cumulative `input_tokens` / `output_tokens` / `cached_input_tokens` totals); a run records the
|
|
7
|
+
* change across its turns. When the provider reported nothing, the count is `null` (unknown),
|
|
8
|
+
* never 0. `token_estimate` is DGC's estimate of the conversation's context size, not billed
|
|
9
|
+
* tokens.
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { appendPrivate, privateDir } from "./state.js";
|
|
14
|
+
export const USAGE_TOTAL_KEYS = [
|
|
15
|
+
"input_tokens", "output_tokens", "cached_input_tokens", "reasoning_tokens", "requests",
|
|
16
|
+
];
|
|
17
|
+
export function emptyTotals() {
|
|
18
|
+
return { input_tokens: 0, output_tokens: 0, cached_input_tokens: 0, reasoning_tokens: 0, requests: 0 };
|
|
19
|
+
}
|
|
20
|
+
export function count(value) {
|
|
21
|
+
if (typeof value === "boolean" || value === null || value === undefined || value === "")
|
|
22
|
+
return null;
|
|
23
|
+
const number = Number(value);
|
|
24
|
+
if (!Number.isFinite(number))
|
|
25
|
+
return null;
|
|
26
|
+
const whole = Math.trunc(number);
|
|
27
|
+
return whole >= 0 ? whole : null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Cost of one usage record, or null without pricing or when the token counts are unknown.
|
|
31
|
+
* `inputTokens` counts every prompt token, cached ones included (as DGC reports them);
|
|
32
|
+
* `cachedInputTokens` is the part served from a cache, billed at the cached price.
|
|
33
|
+
*/
|
|
34
|
+
export function costUsd(inputTokens, outputTokens, cachedInputTokens, pricing) {
|
|
35
|
+
if (!pricing || inputTokens === null || outputTokens === null)
|
|
36
|
+
return null;
|
|
37
|
+
const totalIn = Math.max(0, inputTokens);
|
|
38
|
+
const cached = Math.min(totalIn, Math.max(0, cachedInputTokens || 0));
|
|
39
|
+
const inputRate = Number(pricing.inputPerMillion || 0);
|
|
40
|
+
const cachedRate = Number(pricing.cachedInputPerMillion || 0) || inputRate;
|
|
41
|
+
const dollars = ((totalIn - cached) / 1_000_000) * inputRate
|
|
42
|
+
+ (cached / 1_000_000) * cachedRate
|
|
43
|
+
+ (Math.max(0, outputTokens) / 1_000_000) * Number(pricing.outputPerMillion || 0);
|
|
44
|
+
return Math.round(dollars * 1e8) / 1e8;
|
|
45
|
+
}
|
|
46
|
+
/** The cumulative session totals a `context` event carries, or null if it has none. */
|
|
47
|
+
export function usageTotals(event) {
|
|
48
|
+
const totals = emptyTotals();
|
|
49
|
+
for (const key of USAGE_TOTAL_KEYS) {
|
|
50
|
+
const number = count(event[key]);
|
|
51
|
+
if (number === null) {
|
|
52
|
+
if (key === "input_tokens" || key === "output_tokens" || key === "requests")
|
|
53
|
+
return null;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
totals[key] = number;
|
|
57
|
+
}
|
|
58
|
+
return totals;
|
|
59
|
+
}
|
|
60
|
+
/** What one turn added, or null when the totals were reset (a new or resumed session). */
|
|
61
|
+
export function usageDelta(before, after) {
|
|
62
|
+
const delta = emptyTotals();
|
|
63
|
+
for (const key of USAGE_TOTAL_KEYS) {
|
|
64
|
+
delta[key] = (after[key] || 0) - (before[key] || 0);
|
|
65
|
+
if (delta[key] < 0)
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
return delta;
|
|
69
|
+
}
|
|
70
|
+
/** False when requests were made but the provider reported no token usage for them. */
|
|
71
|
+
export function reported(delta) {
|
|
72
|
+
if ((delta.requests || 0) <= 0)
|
|
73
|
+
return true;
|
|
74
|
+
return Boolean(delta.input_tokens || delta.output_tokens);
|
|
75
|
+
}
|
|
76
|
+
function rowKnown(row) {
|
|
77
|
+
return count(row.input_tokens) !== null && count(row.output_tokens) !== null;
|
|
78
|
+
}
|
|
79
|
+
/** Append-only JSONL under the isolated stateDir. Queryable without dgc serve. */
|
|
80
|
+
export class UsageLog {
|
|
81
|
+
directory;
|
|
82
|
+
path;
|
|
83
|
+
constructor(directory) {
|
|
84
|
+
this.directory = privateDir(directory);
|
|
85
|
+
this.path = join(this.directory, "usage.jsonl");
|
|
86
|
+
}
|
|
87
|
+
record(row) {
|
|
88
|
+
const payload = { ts: Date.now() / 1000, ...row };
|
|
89
|
+
appendPrivate(this.path, JSON.stringify(payload) + "\n");
|
|
90
|
+
}
|
|
91
|
+
query(options = {}) {
|
|
92
|
+
const rows = [];
|
|
93
|
+
if (existsSync(this.path)) {
|
|
94
|
+
for (const line of readFileSync(this.path, "utf8").split("\n")) {
|
|
95
|
+
if (!line.trim())
|
|
96
|
+
continue;
|
|
97
|
+
let row;
|
|
98
|
+
try {
|
|
99
|
+
row = JSON.parse(line);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (!row || typeof row !== "object" || Array.isArray(row))
|
|
105
|
+
continue;
|
|
106
|
+
const record = row;
|
|
107
|
+
if (options.department && String(record.department || "") !== options.department)
|
|
108
|
+
continue;
|
|
109
|
+
const ts = Number(record.ts || 0);
|
|
110
|
+
if (options.since !== undefined && ts < options.since)
|
|
111
|
+
continue;
|
|
112
|
+
if (options.until !== undefined && ts > options.until)
|
|
113
|
+
continue;
|
|
114
|
+
rows.push(record);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const known = rows.filter(rowKnown);
|
|
118
|
+
const costs = known.map((row) => row.cost_usd).filter((value) => typeof value === "number");
|
|
119
|
+
const buckets = new Map();
|
|
120
|
+
for (const row of rows) {
|
|
121
|
+
const key = String(row.department || "");
|
|
122
|
+
let slot = buckets.get(key);
|
|
123
|
+
if (!slot) {
|
|
124
|
+
slot = { department: key, runs: 0, unknownUsageRuns: 0, inputTokens: 0, outputTokens: 0, costUsd: 0 };
|
|
125
|
+
buckets.set(key, slot);
|
|
126
|
+
}
|
|
127
|
+
slot.runs += 1;
|
|
128
|
+
if (!rowKnown(row)) {
|
|
129
|
+
slot.unknownUsageRuns += 1;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
slot.inputTokens += count(row.input_tokens) || 0;
|
|
133
|
+
slot.outputTokens += count(row.output_tokens) || 0;
|
|
134
|
+
if (typeof row.cost_usd === "number")
|
|
135
|
+
slot.costUsd += row.cost_usd;
|
|
136
|
+
}
|
|
137
|
+
const sum = (key) => known.reduce((total, row) => total + (count(row[key]) || 0), 0);
|
|
138
|
+
return {
|
|
139
|
+
runs: rows.length,
|
|
140
|
+
unknownUsageRuns: rows.length - known.length,
|
|
141
|
+
inputTokens: sum("input_tokens"),
|
|
142
|
+
outputTokens: sum("output_tokens"),
|
|
143
|
+
cachedInputTokens: sum("cached_input_tokens"),
|
|
144
|
+
costUsd: costs.length ? Math.round(costs.reduce((a, b) => a + b, 0) * 1e8) / 1e8 : null,
|
|
145
|
+
byDepartment: [...buckets.values()],
|
|
146
|
+
rows: rows.slice(-500),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vibedgc/sdk",
|
|
3
|
+
"version": "0.6.4",
|
|
4
|
+
"description": "Embed the DGC coding harness in applications and CI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "node scripts/build.mjs",
|
|
22
|
+
"prepack": "node scripts/build.mjs"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=22"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "22.20.3",
|
|
29
|
+
"typescript": "5.9.3"
|
|
30
|
+
},
|
|
31
|
+
"keywords": ["dgc", "agent", "sdk", "coding-agent"],
|
|
32
|
+
"homepage": "https://vibedgc.com/sdk/",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/OpenPeach-ai/dgc.git",
|
|
36
|
+
"directory": "sdk/typescript"
|
|
37
|
+
},
|
|
38
|
+
"bugs": "https://github.com/OpenPeach-ai/dgc/issues",
|
|
39
|
+
"license": "Apache-2.0"
|
|
40
|
+
}
|