@linkshell/wire 0.2.29 → 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/relay.d.ts +1 -1
- package/dist/rpc.d.ts +242 -7
- package/dist/rpc.js +97 -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/relay.ts +1 -1
- package/src/rpc.ts +108 -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/relay.ts
CHANGED
|
@@ -123,7 +123,7 @@ export interface RelayMethods {
|
|
|
123
123
|
"machines.forget": { params: { machineId: string }; result: Record<string, never> };
|
|
124
124
|
"devices.list": { params: Record<string, never>; result: { devices: DeviceEntry[] } };
|
|
125
125
|
"devices.revoke": { params: { deviceId: string }; result: Record<string, never> };
|
|
126
|
-
/**
|
|
126
|
+
/** Take this peer off its account (signing out); explicit pairings stay. */
|
|
127
127
|
"account.leave": { params: Record<string, never>; result: Record<string, never> };
|
|
128
128
|
}
|
|
129
129
|
export type RelayMethod = keyof RelayMethods;
|
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) }),
|
|
@@ -480,6 +531,8 @@ const coreMethods = {
|
|
|
480
531
|
* host connects, moves or disconnects without a restart.
|
|
481
532
|
*/
|
|
482
533
|
"gateway.refresh": { params: empty, result: gatewayStatusSchema },
|
|
534
|
+
/** Explicitly end this computer's gateway account binding (local only). */
|
|
535
|
+
"gateway.account.leave": { params: empty, result: empty },
|
|
483
536
|
/**
|
|
484
537
|
* Opens a pairing window (local only). `link` is what the QR encodes; the
|
|
485
538
|
* code can be typed instead. `pairing.done` follows when a device pairs.
|
|
@@ -506,9 +559,24 @@ const coreMethods = {
|
|
|
506
559
|
params: z.object({ password: z.string().min(4).max(128).optional() }),
|
|
507
560
|
result: z.object({ password: z.boolean() }),
|
|
508
561
|
},
|
|
509
|
-
// ── Git: sessions in worktrees ──
|
|
562
|
+
// ── Git: sessions in worktrees & working-tree diffs ──
|
|
510
563
|
/** What git says about `path`; no `git` when it isn't in a repository. */
|
|
511
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
|
+
},
|
|
512
580
|
/** The worktrees LinkShell made, newest first. */
|
|
513
581
|
"worktrees.list": { params: empty, result: z.object({ worktrees: z.array(worktreeEntrySchema) }) },
|
|
514
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`. */
|
|
@@ -598,6 +666,27 @@ const coreMethods = {
|
|
|
598
666
|
params: z.object({ query: z.string().min(1).max(200), limit: z.number().int().min(1).max(200).default(60) }),
|
|
599
667
|
result: z.object({ entries: z.array(directoryEntrySchema), truncated: z.boolean() }),
|
|
600
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
|
+
},
|
|
601
690
|
// ── Terminals: plain shells on the host, any command, like SSH ──
|
|
602
691
|
"terminals.list": {
|
|
603
692
|
params: empty,
|
|
@@ -731,6 +820,14 @@ const coreMethods = {
|
|
|
731
820
|
problem: z.string().optional(),
|
|
732
821
|
}),
|
|
733
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
|
+
},
|
|
734
831
|
/** Bytes to the host's end of a stream (base64). */
|
|
735
832
|
"proxy.write": {
|
|
736
833
|
params: z.object({ streamId: z.string().min(1), data: z.string().max(2_000_000) }),
|
|
@@ -742,7 +839,12 @@ const coreMethods = {
|
|
|
742
839
|
},
|
|
743
840
|
} as const;
|
|
744
841
|
|
|
745
|
-
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
|
+
};
|
|
746
848
|
|
|
747
849
|
export type MethodName = keyof typeof methods;
|
|
748
850
|
export type MethodParams<M extends MethodName> = z.input<(typeof methods)[M]["params"]>;
|
|
@@ -787,6 +889,8 @@ export const notifications = {
|
|
|
787
889
|
"proxy.data": z.object({ streamId: z.string(), data: z.string() }),
|
|
788
890
|
/** The host's end of a stream closed (the server hung up, or refused). */
|
|
789
891
|
"proxy.closed": z.object({ streamId: z.string(), error: z.string().optional() }),
|
|
892
|
+
/** Zero-plaintext transition wakeup metadata. */
|
|
893
|
+
"liveActivities.wakeup": wakeupNoticeSchema,
|
|
790
894
|
} as const;
|
|
791
895
|
|
|
792
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
|
+
}
|