@linkshell/wire 0.2.30 → 0.2.31
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/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/live-activity.d.ts +59 -2
- package/dist/live-activity.js +26 -0
- package/dist/live-activity.js.map +1 -1
- package/dist/lsa.d.ts +308 -0
- package/dist/lsa.js +144 -0
- package/dist/lsa.js.map +1 -0
- package/dist/model.d.ts +66 -0
- package/dist/model.js +19 -0
- package/dist/model.js.map +1 -1
- package/dist/rpc.d.ts +237 -7
- package/dist/rpc.js +95 -5
- package/dist/rpc.js.map +1 -1
- package/dist/updates.d.ts +3 -3
- package/dist/usage.d.ts +32 -0
- package/dist/usage.js +93 -5
- package/dist/usage.js.map +1 -1
- package/dist/watch.d.ts +7 -7
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/live-activity.ts +35 -0
- package/src/lsa.ts +159 -0
- package/src/model.ts +25 -0
- package/src/rpc.ts +106 -4
- package/src/usage.ts +117 -5
package/src/lsa.ts
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* LinkShell Agent (LSA) Phase 1 RPC contracts: deterministic task ledger,
|
|
5
|
+
* automated post-turn acceptance verification, memories, and due followups.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const lsaTaskStatusSchema = z.enum([
|
|
9
|
+
"queued",
|
|
10
|
+
"running",
|
|
11
|
+
"verifying",
|
|
12
|
+
"waiting_decision",
|
|
13
|
+
"completed",
|
|
14
|
+
"failed",
|
|
15
|
+
"cancelled",
|
|
16
|
+
]);
|
|
17
|
+
export type LsaTaskStatus = z.infer<typeof lsaTaskStatusSchema>;
|
|
18
|
+
|
|
19
|
+
export const lsaDecisionSchema = z.enum(["accept", "retry", "cancel"]);
|
|
20
|
+
export type LsaDecision = z.infer<typeof lsaDecisionSchema>;
|
|
21
|
+
|
|
22
|
+
export const lsaTaskSchema = z.object({
|
|
23
|
+
id: z.string().min(1),
|
|
24
|
+
title: z.string().optional(),
|
|
25
|
+
goal: z.string().min(1),
|
|
26
|
+
status: lsaTaskStatusSchema,
|
|
27
|
+
cwd: z.string(),
|
|
28
|
+
worktree: z.boolean().optional(),
|
|
29
|
+
agent: z.string().optional(),
|
|
30
|
+
sessionId: z.string().optional(),
|
|
31
|
+
controllerEpoch: z.number().int().nonnegative().optional(),
|
|
32
|
+
acceptanceChecks: z.array(z.string()),
|
|
33
|
+
summary: z.string().optional(),
|
|
34
|
+
createdAt: z.number(),
|
|
35
|
+
updatedAt: z.number(),
|
|
36
|
+
});
|
|
37
|
+
export type LsaTask = z.infer<typeof lsaTaskSchema>;
|
|
38
|
+
|
|
39
|
+
export const lsaMemorySchema = z.object({
|
|
40
|
+
id: z.string().optional(),
|
|
41
|
+
scope: z.string().optional(),
|
|
42
|
+
key: z.string().min(1),
|
|
43
|
+
content: z.string(),
|
|
44
|
+
tags: z.array(z.string()),
|
|
45
|
+
updatedAt: z.number(),
|
|
46
|
+
});
|
|
47
|
+
export type LsaMemory = z.infer<typeof lsaMemorySchema>;
|
|
48
|
+
|
|
49
|
+
export const lsaFollowupSchema = z.object({
|
|
50
|
+
id: z.string().min(1),
|
|
51
|
+
taskId: z.string().optional(),
|
|
52
|
+
dueAt: z.number(),
|
|
53
|
+
note: z.string(),
|
|
54
|
+
done: z.boolean(),
|
|
55
|
+
triggeredAt: z.number().optional(),
|
|
56
|
+
});
|
|
57
|
+
export type LsaFollowup = z.infer<typeof lsaFollowupSchema>;
|
|
58
|
+
|
|
59
|
+
export const lsaCandidateProjectSchema = z.object({
|
|
60
|
+
cwd: z.string(),
|
|
61
|
+
name: z.string(),
|
|
62
|
+
score: z.number(),
|
|
63
|
+
remote: z.string().optional(),
|
|
64
|
+
reason: z.string().optional(),
|
|
65
|
+
});
|
|
66
|
+
export type LsaCandidateProject = z.infer<typeof lsaCandidateProjectSchema>;
|
|
67
|
+
|
|
68
|
+
export const lsaStatusSchema = z.object({
|
|
69
|
+
enabled: z.boolean(),
|
|
70
|
+
controllerEpoch: z.number().int().nonnegative(),
|
|
71
|
+
generation: z.number().int().positive(),
|
|
72
|
+
activeTasks: z.number().int().nonnegative(),
|
|
73
|
+
waitingDecisionTasks: z.number().int().nonnegative(),
|
|
74
|
+
dueFollowups: z.number().int().nonnegative(),
|
|
75
|
+
requiresYearlyTier: z.boolean(),
|
|
76
|
+
handoffSummary: z.string().optional(),
|
|
77
|
+
candidates: z.array(lsaCandidateProjectSchema).optional(),
|
|
78
|
+
});
|
|
79
|
+
export type LsaStatus = z.infer<typeof lsaStatusSchema>;
|
|
80
|
+
|
|
81
|
+
export const lsaMethods = {
|
|
82
|
+
"lsa.status": {
|
|
83
|
+
params: z.object({
|
|
84
|
+
query: z.string().max(200).optional(),
|
|
85
|
+
}),
|
|
86
|
+
result: lsaStatusSchema,
|
|
87
|
+
},
|
|
88
|
+
"lsa.tasks": {
|
|
89
|
+
params: z.object({
|
|
90
|
+
taskId: z.string().optional(),
|
|
91
|
+
status: lsaTaskStatusSchema.optional(),
|
|
92
|
+
limit: z.number().int().positive().max(200).optional(),
|
|
93
|
+
}),
|
|
94
|
+
result: z.object({
|
|
95
|
+
tasks: z.array(lsaTaskSchema),
|
|
96
|
+
}),
|
|
97
|
+
},
|
|
98
|
+
"lsa.dispatch": {
|
|
99
|
+
params: z.object({
|
|
100
|
+
goal: z.string().min(1).max(20_000),
|
|
101
|
+
title: z.string().max(200).optional(),
|
|
102
|
+
cwd: z.string().optional(),
|
|
103
|
+
agent: z.string().optional(),
|
|
104
|
+
worktree: z.boolean().optional(),
|
|
105
|
+
acceptanceChecks: z.array(z.string().max(2000)).max(50).optional(),
|
|
106
|
+
}),
|
|
107
|
+
result: z.object({
|
|
108
|
+
task: lsaTaskSchema,
|
|
109
|
+
}),
|
|
110
|
+
},
|
|
111
|
+
"lsa.steer": {
|
|
112
|
+
params: z.object({
|
|
113
|
+
taskId: z.string().min(1),
|
|
114
|
+
instruction: z.string().min(1).max(20_000),
|
|
115
|
+
controllerEpoch: z.number().int().nonnegative().optional(),
|
|
116
|
+
}),
|
|
117
|
+
result: z.object({
|
|
118
|
+
task: lsaTaskSchema,
|
|
119
|
+
}),
|
|
120
|
+
},
|
|
121
|
+
"lsa.decide": {
|
|
122
|
+
params: z.object({
|
|
123
|
+
taskId: z.string().min(1),
|
|
124
|
+
decision: lsaDecisionSchema,
|
|
125
|
+
feedback: z.string().max(20_000).optional(),
|
|
126
|
+
controllerEpoch: z.number().int().nonnegative().optional(),
|
|
127
|
+
}),
|
|
128
|
+
result: z.object({
|
|
129
|
+
task: lsaTaskSchema,
|
|
130
|
+
}),
|
|
131
|
+
},
|
|
132
|
+
"lsa.memories": {
|
|
133
|
+
params: z.object({
|
|
134
|
+
op: z.enum(["list", "search", "save", "delete"]).optional(),
|
|
135
|
+
query: z.string().max(500).optional(),
|
|
136
|
+
scope: z.string().max(120).optional(),
|
|
137
|
+
key: z.string().max(200).optional(),
|
|
138
|
+
content: z.string().max(20_000).optional(),
|
|
139
|
+
tags: z.array(z.string().max(80)).max(32).optional(),
|
|
140
|
+
limit: z.number().int().positive().max(200).optional(),
|
|
141
|
+
}),
|
|
142
|
+
result: z.object({
|
|
143
|
+
memories: z.array(lsaMemorySchema),
|
|
144
|
+
}),
|
|
145
|
+
},
|
|
146
|
+
"lsa.followups": {
|
|
147
|
+
params: z.object({
|
|
148
|
+
op: z.enum(["list", "schedule", "complete", "delete"]).optional(),
|
|
149
|
+
id: z.string().optional(),
|
|
150
|
+
taskId: z.string().optional(),
|
|
151
|
+
dueAt: z.number().optional(),
|
|
152
|
+
note: z.string().max(2000).optional(),
|
|
153
|
+
includeDone: z.boolean().optional(),
|
|
154
|
+
}),
|
|
155
|
+
result: z.object({
|
|
156
|
+
followups: z.array(lsaFollowupSchema),
|
|
157
|
+
}),
|
|
158
|
+
},
|
|
159
|
+
} as const;
|
package/src/model.ts
CHANGED
|
@@ -302,6 +302,10 @@ export const gitInfoSchema = z.object({
|
|
|
302
302
|
head: z.string().optional(),
|
|
303
303
|
/** Uncommitted changes in the working directory (a new worktree starts from the last commit, without them). */
|
|
304
304
|
dirty: z.boolean(),
|
|
305
|
+
/** Normalized git origin remote (`host/owner/repo`), when configured. */
|
|
306
|
+
remote: z.string().optional(),
|
|
307
|
+
/** First commit hash of the repository, for cross-machine repo identity. */
|
|
308
|
+
rootCommit: z.string().optional(),
|
|
305
309
|
});
|
|
306
310
|
export type GitInfo = z.infer<typeof gitInfoSchema>;
|
|
307
311
|
|
|
@@ -356,6 +360,23 @@ export const machineInfoSchema = z.object({
|
|
|
356
360
|
});
|
|
357
361
|
export type MachineInfo = z.infer<typeof machineInfoSchema>;
|
|
358
362
|
|
|
363
|
+
export const keepAwakeModeSchema = z.enum(["smart", "always", "off"]);
|
|
364
|
+
export type KeepAwakeMode = z.infer<typeof keepAwakeModeSchema>;
|
|
365
|
+
|
|
366
|
+
export const machinePowerSchema = z.object({
|
|
367
|
+
onBattery: z.boolean(),
|
|
368
|
+
percent: z.number().min(0).max(100).nullable(),
|
|
369
|
+
charging: z.boolean().optional(),
|
|
370
|
+
});
|
|
371
|
+
export type MachinePower = z.infer<typeof machinePowerSchema>;
|
|
372
|
+
|
|
373
|
+
export const machineKeepAwakeSchema = z.object({
|
|
374
|
+
mode: keepAwakeModeSchema,
|
|
375
|
+
active: z.boolean(),
|
|
376
|
+
reason: z.string().optional(),
|
|
377
|
+
});
|
|
378
|
+
export type MachineKeepAwake = z.infer<typeof machineKeepAwakeSchema>;
|
|
379
|
+
|
|
359
380
|
/** Whole-computer resource use; CPU is a delta across all logical cores, not load average. */
|
|
360
381
|
export const machineMetricsSchema = z.object({
|
|
361
382
|
sampledAt: z.number().nonnegative(),
|
|
@@ -382,6 +403,10 @@ export const machineMetricsSchema = z.object({
|
|
|
382
403
|
/** Space available to this user; excludes filesystem reservations. */
|
|
383
404
|
availableBytes: z.number().nonnegative(),
|
|
384
405
|
}).optional(),
|
|
406
|
+
/** Battery and AC power state on laptops; absent on desktops or older hosts. */
|
|
407
|
+
power: machinePowerSchema.optional(),
|
|
408
|
+
/** Host idle-sleep assertion state; absent on older hosts. */
|
|
409
|
+
keepAwake: machineKeepAwakeSchema.optional(),
|
|
385
410
|
});
|
|
386
411
|
export type MachineMetrics = z.infer<typeof machineMetricsSchema>;
|
|
387
412
|
|
package/src/rpc.ts
CHANGED
|
@@ -3,12 +3,14 @@ import { usageQuerySchema, usageReportSchema } from "./usage.js";
|
|
|
3
3
|
import { goalChangeSchema, sessionGoalSchema } from "./goal.js";
|
|
4
4
|
import { acpAgentSettingsSchema, acpFeaturesSchema, acpRemoteAgentSchema, acpProviderSchema } from "./acp.js";
|
|
5
5
|
import { editorRequestSchema, editorResultSchema } from "./editor.js";
|
|
6
|
-
import { liveActivityIdSchema, liveActivitySubscriptionSchema } from "./live-activity.js";
|
|
6
|
+
import { liveActivityIdSchema, liveActivityPushToStartSchema, liveActivitySubscriptionSchema, wakeupNoticeSchema } from "./live-activity.js";
|
|
7
7
|
import {
|
|
8
8
|
backgroundTaskSchema,
|
|
9
9
|
agentInfoSchema,
|
|
10
10
|
gatewayStatusSchema,
|
|
11
|
+
keepAwakeModeSchema,
|
|
11
12
|
machineInfoSchema,
|
|
13
|
+
machineKeepAwakeSchema,
|
|
12
14
|
machineMetricsSnapshotSchema,
|
|
13
15
|
portInfoSchema,
|
|
14
16
|
projectSummarySchema,
|
|
@@ -24,6 +26,7 @@ import {
|
|
|
24
26
|
import { contentBlockSchema, sessionConfigOptionSchema, sessionEventSchema, sessionNoticeSchema } from "./updates.js";
|
|
25
27
|
import { sideChatSchema } from "./side.js";
|
|
26
28
|
import { mcpMethods } from "./rpc-mcp.js";
|
|
29
|
+
import { lsaMethods } from "./lsa.js";
|
|
27
30
|
|
|
28
31
|
// ── JSON-RPC 2.0 framing ─────────────────────────────────────────────
|
|
29
32
|
|
|
@@ -77,11 +80,51 @@ export const RPC_APP_ERROR = -32000;
|
|
|
77
80
|
* 1: side conversations (`sessions.side`), `machine.info.protocol` itself.
|
|
78
81
|
* 2: session terminals (`terminals.create` `sessionId`, `TerminalInfo.sessionId`).
|
|
79
82
|
* 3: whole-computer CPU and physical memory sampling (`machine.metrics`).
|
|
83
|
+
* 4: smart keep-awake & battery telemetry (`machine.keepAwake`), real git working-tree
|
|
84
|
+
* diff/revert (`git.diff`, `git.revertFile`), fast `@file` search (`fs.projectFiles`),
|
|
85
|
+
* clipboard & screen privacy (`clipboard.*`, `screen.privacy`), push-to-start wakeup
|
|
86
|
+
* (`liveActivities.registerPushToStart`), and LSA Phase 1 steward RPCs (`lsa.*`).
|
|
80
87
|
*/
|
|
81
|
-
export const HOST_PROTOCOL =
|
|
88
|
+
export const HOST_PROTOCOL = 4;
|
|
82
89
|
|
|
83
90
|
const empty = z.looseObject({});
|
|
84
91
|
|
|
92
|
+
export const gitDiffFileStatusSchema = z.enum(["modified", "added", "deleted", "renamed", "untracked"]);
|
|
93
|
+
export type GitDiffFileStatus = z.infer<typeof gitDiffFileStatusSchema>;
|
|
94
|
+
|
|
95
|
+
export const gitDiffFileSchema = z.object({
|
|
96
|
+
path: z.string(),
|
|
97
|
+
oldPath: z.string().optional(),
|
|
98
|
+
status: gitDiffFileStatusSchema,
|
|
99
|
+
oldText: z.string(),
|
|
100
|
+
newText: z.string(),
|
|
101
|
+
additions: z.number().int().nonnegative(),
|
|
102
|
+
deletions: z.number().int().nonnegative(),
|
|
103
|
+
binary: z.boolean().optional(),
|
|
104
|
+
});
|
|
105
|
+
export type GitDiffFile = z.infer<typeof gitDiffFileSchema>;
|
|
106
|
+
|
|
107
|
+
export const gitDiffResultSchema = z.object({
|
|
108
|
+
files: z.array(gitDiffFileSchema),
|
|
109
|
+
truncated: z.boolean(),
|
|
110
|
+
branch: z.string().optional(),
|
|
111
|
+
});
|
|
112
|
+
export type GitDiffResult = z.infer<typeof gitDiffResultSchema>;
|
|
113
|
+
|
|
114
|
+
export const projectFileEntrySchema = z.object({
|
|
115
|
+
path: z.string(),
|
|
116
|
+
relativePath: z.string(),
|
|
117
|
+
isDir: z.boolean().optional(),
|
|
118
|
+
});
|
|
119
|
+
export type ProjectFileEntry = z.infer<typeof projectFileEntrySchema>;
|
|
120
|
+
|
|
121
|
+
export const screenPrivacyStateSchema = z.object({
|
|
122
|
+
curtain: z.boolean(),
|
|
123
|
+
lockOnDisconnect: z.boolean(),
|
|
124
|
+
supported: z.boolean(),
|
|
125
|
+
});
|
|
126
|
+
export type ScreenPrivacyState = z.infer<typeof screenPrivacyStateSchema>;
|
|
127
|
+
|
|
85
128
|
export const directoryEntrySchema = z.object({
|
|
86
129
|
name: z.string(),
|
|
87
130
|
path: z.string(),
|
|
@@ -133,11 +176,19 @@ const coreMethods = {
|
|
|
133
176
|
params: z.object({ activityId: liveActivityIdSchema }),
|
|
134
177
|
result: z.object({}),
|
|
135
178
|
},
|
|
179
|
+
"liveActivities.registerPushToStart": {
|
|
180
|
+
params: liveActivityPushToStartSchema,
|
|
181
|
+
result: z.looseObject({ ok: z.boolean().optional() }),
|
|
182
|
+
},
|
|
136
183
|
"machine.info": {
|
|
137
184
|
params: empty,
|
|
138
185
|
result: machineInfoSchema,
|
|
139
186
|
},
|
|
140
187
|
"machine.metrics": { params: empty, result: machineMetricsSnapshotSchema },
|
|
188
|
+
"machine.keepAwake": {
|
|
189
|
+
params: z.object({ mode: keepAwakeModeSchema.optional() }),
|
|
190
|
+
result: machineKeepAwakeSchema,
|
|
191
|
+
},
|
|
141
192
|
"machine.rename": {
|
|
142
193
|
/** Empty restores the configured default; names never change peer identities. */
|
|
143
194
|
params: z.object({ name: z.string().trim().max(40) }),
|
|
@@ -508,9 +559,24 @@ const coreMethods = {
|
|
|
508
559
|
params: z.object({ password: z.string().min(4).max(128).optional() }),
|
|
509
560
|
result: z.object({ password: z.boolean() }),
|
|
510
561
|
},
|
|
511
|
-
// ── Git: sessions in worktrees ──
|
|
562
|
+
// ── Git: sessions in worktrees & working-tree diffs ──
|
|
512
563
|
/** What git says about `path`; no `git` when it isn't in a repository. */
|
|
513
564
|
"git.info": { params: z.object({ path: z.string().min(1) }), result: z.object({ git: gitInfoSchema.optional() }) },
|
|
565
|
+
/** Real git working-tree diff (or against `base`), bounded by `maxBytes` and including untracked text files by default. */
|
|
566
|
+
"git.diff": {
|
|
567
|
+
params: z.object({
|
|
568
|
+
cwd: z.string().min(1),
|
|
569
|
+
base: z.string().optional(),
|
|
570
|
+
includeUntracked: z.boolean().optional(),
|
|
571
|
+
maxBytes: z.number().int().positive().max(10_000_000).optional(),
|
|
572
|
+
}),
|
|
573
|
+
result: gitDiffResultSchema,
|
|
574
|
+
},
|
|
575
|
+
/** Restores a tracked file or removes an untracked file strictly inside the repository root. */
|
|
576
|
+
"git.revertFile": {
|
|
577
|
+
params: z.object({ cwd: z.string().min(1), path: z.string().min(1) }),
|
|
578
|
+
result: z.object({ ok: z.boolean() }),
|
|
579
|
+
},
|
|
514
580
|
/** The worktrees LinkShell made, newest first. */
|
|
515
581
|
"worktrees.list": { params: empty, result: z.object({ worktrees: z.array(worktreeEntrySchema) }) },
|
|
516
582
|
/** Removes a worktree and its branch. Refused (`busy`) while a session uses it, or (`dirty`) when work in it would be lost, unless `force`. */
|
|
@@ -600,6 +666,27 @@ const coreMethods = {
|
|
|
600
666
|
params: z.object({ query: z.string().min(1).max(200), limit: z.number().int().min(1).max(200).default(60) }),
|
|
601
667
|
result: z.object({ entries: z.array(directoryEntrySchema), truncated: z.boolean() }),
|
|
602
668
|
},
|
|
669
|
+
/** Fast project file listing under `cwd` (`git ls-files` with directory walk fallback) for `@file` autocomplete. */
|
|
670
|
+
"fs.projectFiles": {
|
|
671
|
+
params: z.object({
|
|
672
|
+
cwd: z.string().min(1),
|
|
673
|
+
query: z.string().max(200).optional(),
|
|
674
|
+
limit: z.number().int().min(1).max(2000).optional(),
|
|
675
|
+
}),
|
|
676
|
+
result: z.object({
|
|
677
|
+
files: z.array(projectFileEntrySchema),
|
|
678
|
+
truncated: z.boolean(),
|
|
679
|
+
}),
|
|
680
|
+
},
|
|
681
|
+
// ── Clipboard: encrypted host clipboard access ──
|
|
682
|
+
"clipboard.read": {
|
|
683
|
+
params: empty,
|
|
684
|
+
result: z.object({ text: z.string() }),
|
|
685
|
+
},
|
|
686
|
+
"clipboard.write": {
|
|
687
|
+
params: z.object({ text: z.string().max(2_000_000) }),
|
|
688
|
+
result: z.object({ ok: z.boolean() }),
|
|
689
|
+
},
|
|
603
690
|
// ── Terminals: plain shells on the host, any command, like SSH ──
|
|
604
691
|
"terminals.list": {
|
|
605
692
|
params: empty,
|
|
@@ -733,6 +820,14 @@ const coreMethods = {
|
|
|
733
820
|
problem: z.string().optional(),
|
|
734
821
|
}),
|
|
735
822
|
},
|
|
823
|
+
/** Privacy curtain (dim/blank physical screen while viewing remotely) and auto-lock on disconnect. */
|
|
824
|
+
"screen.privacy": {
|
|
825
|
+
params: z.object({
|
|
826
|
+
curtain: z.boolean().optional(),
|
|
827
|
+
lockOnDisconnect: z.boolean().optional(),
|
|
828
|
+
}),
|
|
829
|
+
result: screenPrivacyStateSchema,
|
|
830
|
+
},
|
|
736
831
|
/** Bytes to the host's end of a stream (base64). */
|
|
737
832
|
"proxy.write": {
|
|
738
833
|
params: z.object({ streamId: z.string().min(1), data: z.string().max(2_000_000) }),
|
|
@@ -744,7 +839,12 @@ const coreMethods = {
|
|
|
744
839
|
},
|
|
745
840
|
} as const;
|
|
746
841
|
|
|
747
|
-
export const methods: typeof coreMethods & typeof acpMethods & typeof mcpMethods
|
|
842
|
+
export const methods: typeof coreMethods & typeof acpMethods & typeof mcpMethods & typeof lsaMethods = {
|
|
843
|
+
...coreMethods,
|
|
844
|
+
...acpMethods,
|
|
845
|
+
...mcpMethods,
|
|
846
|
+
...lsaMethods,
|
|
847
|
+
};
|
|
748
848
|
|
|
749
849
|
export type MethodName = keyof typeof methods;
|
|
750
850
|
export type MethodParams<M extends MethodName> = z.input<(typeof methods)[M]["params"]>;
|
|
@@ -789,6 +889,8 @@ export const notifications = {
|
|
|
789
889
|
"proxy.data": z.object({ streamId: z.string(), data: z.string() }),
|
|
790
890
|
/** The host's end of a stream closed (the server hung up, or refused). */
|
|
791
891
|
"proxy.closed": z.object({ streamId: z.string(), error: z.string().optional() }),
|
|
892
|
+
/** Zero-plaintext transition wakeup metadata. */
|
|
893
|
+
"liveActivities.wakeup": wakeupNoticeSchema,
|
|
792
894
|
} as const;
|
|
793
895
|
|
|
794
896
|
export type NotificationName = keyof typeof notifications;
|
package/src/usage.ts
CHANGED
|
@@ -4,21 +4,133 @@ export const usageQuerySchema = z.object({
|
|
|
4
4
|
days: z.union([z.literal(1), z.literal(7), z.literal(30)]).default(7),
|
|
5
5
|
agent: z.string().min(1).max(128).default("all"),
|
|
6
6
|
});
|
|
7
|
+
|
|
7
8
|
export const usageTotalsSchema = z.object({
|
|
8
|
-
records: z.number(),
|
|
9
|
-
|
|
9
|
+
records: z.number(),
|
|
10
|
+
sessions: z.number(),
|
|
11
|
+
inputTokens: z.number(),
|
|
12
|
+
outputTokens: z.number(),
|
|
13
|
+
cacheReadTokens: z.number(),
|
|
14
|
+
cacheWriteTokens: z.number(),
|
|
15
|
+
totalTokens: z.number(),
|
|
16
|
+
estimatedCostUsd: z.number().nonnegative().optional(),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const usageRemoteStatsSchema = z.object({
|
|
20
|
+
remoteTurns: z.number().int().nonnegative(),
|
|
21
|
+
remoteApprovals: z.number().int().nonnegative(),
|
|
10
22
|
});
|
|
23
|
+
|
|
11
24
|
export const usageReportSchema = z.object({
|
|
12
|
-
generatedAt: z.number(),
|
|
25
|
+
generatedAt: z.number(),
|
|
26
|
+
days: z.number(),
|
|
27
|
+
agent: z.string(),
|
|
28
|
+
timezone: z.string(),
|
|
13
29
|
totals: usageTotalsSchema,
|
|
14
30
|
daily: z.array(usageTotalsSchema.extend({ date: z.string() })),
|
|
15
31
|
models: z.array(usageTotalsSchema.extend({ agent: z.string(), model: z.string() })),
|
|
16
32
|
agents: z.array(z.object({
|
|
17
|
-
id: z.string(),
|
|
18
|
-
|
|
33
|
+
id: z.string(),
|
|
34
|
+
label: z.string(),
|
|
35
|
+
source: z.enum(["native", "acp"]),
|
|
36
|
+
status: z.enum(["available", "no_data"]),
|
|
37
|
+
records: z.number(),
|
|
38
|
+
totalTokens: z.number(),
|
|
39
|
+
note: z.string(),
|
|
40
|
+
estimatedCostUsd: z.number().nonnegative().optional(),
|
|
19
41
|
})).optional(),
|
|
42
|
+
estimatedCostUsd: z.number().nonnegative().optional(),
|
|
43
|
+
remoteStats: usageRemoteStatsSchema.optional(),
|
|
20
44
|
warnings: z.array(z.string()),
|
|
21
45
|
});
|
|
46
|
+
|
|
22
47
|
export type UsageQuery = z.infer<typeof usageQuerySchema>;
|
|
23
48
|
export type UsageTotals = z.infer<typeof usageTotalsSchema>;
|
|
49
|
+
export type UsageRemoteStats = z.infer<typeof usageRemoteStatsSchema>;
|
|
24
50
|
export type UsageReport = z.infer<typeof usageReportSchema>;
|
|
51
|
+
|
|
52
|
+
export interface ModelPricingPerMillion {
|
|
53
|
+
input: number;
|
|
54
|
+
output: number;
|
|
55
|
+
cacheRead: number;
|
|
56
|
+
cacheWrite: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface TokenCostInput {
|
|
60
|
+
inputTokens: number;
|
|
61
|
+
outputTokens: number;
|
|
62
|
+
cacheReadTokens?: number;
|
|
63
|
+
cacheWriteTokens?: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Reference public API pricing (USD per 1M tokens), ordered most-specific first.
|
|
68
|
+
* Subscription users (ChatGPT Plus/Pro, Claude Pro/Max) pay flat fees; this
|
|
69
|
+
* estimates the equivalent API value of work orchestrated through LinkShell.
|
|
70
|
+
*/
|
|
71
|
+
const MODEL_PRICING_RULES: ReadonlyArray<{ match: RegExp; pricing: ModelPricingPerMillion }> = [
|
|
72
|
+
// Anthropic Claude
|
|
73
|
+
{ match: /claude[-_.]?opus[-_.]?(?:4[._][56]|5)/i, pricing: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 } },
|
|
74
|
+
{ match: /opus/i, pricing: { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 } },
|
|
75
|
+
{ match: /sonnet/i, pricing: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 } },
|
|
76
|
+
{ match: /claude[-_.]?haiku[-_.]?4[._]5/i, pricing: { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 } },
|
|
77
|
+
{ match: /haiku/i, pricing: { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 } },
|
|
78
|
+
// OpenAI / Codex
|
|
79
|
+
{ match: /(?:gpt-5|o1|o3)[-_]pro/i, pricing: { input: 15, output: 60, cacheRead: 3.75, cacheWrite: 15 } },
|
|
80
|
+
{ match: /gpt-5[-_.]?mini/i, pricing: { input: 0.25, output: 2, cacheRead: 0.025, cacheWrite: 0.25 } },
|
|
81
|
+
{ match: /gpt-5[-_.]?nano/i, pricing: { input: 0.05, output: 0.4, cacheRead: 0.005, cacheWrite: 0.05 } },
|
|
82
|
+
{ match: /codex[-_.]?mini/i, pricing: { input: 1.5, output: 6, cacheRead: 0.375, cacheWrite: 1.5 } },
|
|
83
|
+
{ match: /gpt-5|codex/i, pricing: { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite: 1.25 } },
|
|
84
|
+
{ match: /gpt-4\.1[-_.]?mini/i, pricing: { input: 0.4, output: 1.6, cacheRead: 0.1, cacheWrite: 0.4 } },
|
|
85
|
+
{ match: /gpt-4\.1[-_.]?nano/i, pricing: { input: 0.1, output: 0.4, cacheRead: 0.025, cacheWrite: 0.1 } },
|
|
86
|
+
{ match: /gpt-4\.1/i, pricing: { input: 2, output: 8, cacheRead: 0.5, cacheWrite: 2 } },
|
|
87
|
+
{ match: /gpt-4o[-_.]?mini/i, pricing: { input: 0.15, output: 0.6, cacheRead: 0.075, cacheWrite: 0.15 } },
|
|
88
|
+
{ match: /gpt-4o/i, pricing: { input: 2.5, output: 10, cacheRead: 1.25, cacheWrite: 2.5 } },
|
|
89
|
+
{ match: /o[34][-_.]?mini/i, pricing: { input: 1.1, output: 4.4, cacheRead: 0.275, cacheWrite: 1.1 } },
|
|
90
|
+
{ match: /\bo3\b|\bo1\b/i, pricing: { input: 2, output: 8, cacheRead: 0.5, cacheWrite: 2 } },
|
|
91
|
+
// Google Gemini
|
|
92
|
+
{ match: /gemini.*flash/i, pricing: { input: 0.3, output: 2.5, cacheRead: 0.075, cacheWrite: 0.3 } },
|
|
93
|
+
{ match: /gemini/i, pricing: { input: 1.25, output: 10, cacheRead: 0.3125, cacheWrite: 1.25 } },
|
|
94
|
+
// xAI Grok & DeepSeek
|
|
95
|
+
{ match: /grok/i, pricing: { input: 3, output: 15, cacheRead: 0.75, cacheWrite: 3 } },
|
|
96
|
+
{ match: /deepseek/i, pricing: { input: 0.27, output: 1.1, cacheRead: 0.07, cacheWrite: 0.27 } },
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
const DEFAULT_PRICING_PER_MILLION: ModelPricingPerMillion = {
|
|
100
|
+
input: 2,
|
|
101
|
+
output: 8,
|
|
102
|
+
cacheRead: 0.2,
|
|
103
|
+
cacheWrite: 2,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export function modelPricingPerMillionUsd(model: string): ModelPricingPerMillion {
|
|
107
|
+
const normalized = model.trim();
|
|
108
|
+
for (const rule of MODEL_PRICING_RULES) {
|
|
109
|
+
if (rule.match.test(normalized)) return rule.pricing;
|
|
110
|
+
}
|
|
111
|
+
return DEFAULT_PRICING_PER_MILLION;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function safeTokenCount(value: number | undefined): number {
|
|
115
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Pure reference cost estimator (in USD) for a model and non-overlapping token bucket totals.
|
|
120
|
+
*/
|
|
121
|
+
export function estimateModelCostUsd(model: string, totals: TokenCostInput): number {
|
|
122
|
+
const input = safeTokenCount(totals.inputTokens);
|
|
123
|
+
const output = safeTokenCount(totals.outputTokens);
|
|
124
|
+
const cacheRead = safeTokenCount(totals.cacheReadTokens);
|
|
125
|
+
const cacheWrite = safeTokenCount(totals.cacheWriteTokens);
|
|
126
|
+
if (input === 0 && output === 0 && cacheRead === 0 && cacheWrite === 0) return 0;
|
|
127
|
+
|
|
128
|
+
const rate = modelPricingPerMillionUsd(model);
|
|
129
|
+
const rawUsd =
|
|
130
|
+
(input * rate.input +
|
|
131
|
+
output * rate.output +
|
|
132
|
+
cacheRead * rate.cacheRead +
|
|
133
|
+
cacheWrite * rate.cacheWrite) /
|
|
134
|
+
1_000_000;
|
|
135
|
+
return Math.round(rawUsd * 1_000_000) / 1_000_000;
|
|
136
|
+
}
|