@love-moon/ai-sdk 0.3.1 → 0.4.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/CHANGELOG.md +18 -0
- package/dist/built-in-backends.d.ts +1 -0
- package/dist/built-in-backends.js +6 -0
- package/dist/client.d.ts +15 -0
- package/dist/client.js +103 -1
- package/dist/external-provider-registry.js +4 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/manager/account.d.ts +6 -0
- package/dist/manager/account.js +121 -0
- package/dist/manager/auth-parser.d.ts +27 -0
- package/dist/manager/auth-parser.js +54 -0
- package/dist/manager/config.d.ts +6 -0
- package/dist/manager/config.js +32 -0
- package/dist/manager/index.d.ts +12 -0
- package/dist/manager/index.js +11 -0
- package/dist/manager/install.d.ts +9 -0
- package/dist/manager/install.js +117 -0
- package/dist/manager/manager.d.ts +51 -0
- package/dist/manager/manager.js +105 -0
- package/dist/manager/network.d.ts +8 -0
- package/dist/manager/network.js +46 -0
- package/dist/manager/paths.d.ts +6 -0
- package/dist/manager/paths.js +16 -0
- package/dist/manager/quota/cache.d.ts +9 -0
- package/dist/manager/quota/cache.js +33 -0
- package/dist/manager/quota/claude.d.ts +19 -0
- package/dist/manager/quota/claude.js +193 -0
- package/dist/manager/quota/codex.d.ts +27 -0
- package/dist/manager/quota/codex.js +182 -0
- package/dist/manager/quota/copilot.d.ts +64 -0
- package/dist/manager/quota/copilot.js +718 -0
- package/dist/manager/quota/external.d.ts +29 -0
- package/dist/manager/quota/external.js +176 -0
- package/dist/manager/quota/headers.d.ts +5 -0
- package/dist/manager/quota/headers.js +29 -0
- package/dist/manager/quota/kimi.d.ts +24 -0
- package/dist/manager/quota/kimi.js +230 -0
- package/dist/manager/types.d.ts +166 -0
- package/dist/manager/types.js +1 -0
- package/dist/providers/chat-web-session.d.ts +218 -0
- package/dist/providers/chat-web-session.js +584 -0
- package/dist/providers/claude-agent-sdk-session.d.ts +35 -1
- package/dist/providers/claude-agent-sdk-session.js +109 -1
- package/dist/providers/codex-app-server-session.d.ts +107 -0
- package/dist/providers/codex-app-server-session.js +479 -9
- package/dist/providers/copilot-sdk-session.d.ts +9 -1
- package/dist/providers/copilot-sdk-session.js +48 -0
- package/dist/resume/chat-web.d.ts +20 -0
- package/dist/resume/chat-web.js +44 -0
- package/dist/resume/index.js +2 -0
- package/dist/session-factory.d.ts +3 -1
- package/dist/session-factory.js +17 -4
- package/dist/shared.d.ts +159 -0
- package/dist/shared.js +111 -0
- package/dist/transports/codex-app-server-transport.d.ts +1 -0
- package/dist/transports/codex-app-server-transport.js +45 -1
- package/dist/worker.js +19 -5
- package/package.json +10 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
2
|
import { CLAUDE_AGENT_SDK_VARIANT as CLAUDE_PROVIDER_VARIANT } from "../built-in-backends.js";
|
|
3
|
-
import { emitLog, getBoundedEnvInt, loadEnvConfig, normalizeLogger, proxyToEnv, sanitizeForLog, } from "../shared.js";
|
|
3
|
+
import { emitLog, getBoundedEnvInt, isGoalStatus, loadEnvConfig, normalizeLogger, proxyToEnv, sanitizeForLog, } from "../shared.js";
|
|
4
4
|
const DEFAULT_TURN_DEADLINE_MS = 12 * 60 * 1000;
|
|
5
5
|
const MIN_TURN_DEADLINE_MS = 30 * 1000;
|
|
6
6
|
const MAX_TURN_DEADLINE_MS = 30 * 60 * 1000;
|
|
@@ -126,6 +126,13 @@ function sanitizeSummary(value, maxLen = 180) {
|
|
|
126
126
|
return sanitizeForLog(value, maxLen);
|
|
127
127
|
}
|
|
128
128
|
export class ClaudeAgentSdkSession extends EventEmitter {
|
|
129
|
+
// Capability advertised via getSnapshot().capabilities so worker proxies
|
|
130
|
+
// can short-circuit runGoal without an IPC round trip. Claude exposes
|
|
131
|
+
// native `/goal` slash command, so this is true.
|
|
132
|
+
static capabilities = Object.freeze({ goal: true });
|
|
133
|
+
getCapabilities() {
|
|
134
|
+
return { ...ClaudeAgentSdkSession.capabilities };
|
|
135
|
+
}
|
|
129
136
|
constructor(backend, options = {}) {
|
|
130
137
|
super();
|
|
131
138
|
this.backend = normalizeClaudeBackend(backend);
|
|
@@ -207,6 +214,7 @@ export class ClaudeAgentSdkSession extends EventEmitter {
|
|
|
207
214
|
}
|
|
208
215
|
: null,
|
|
209
216
|
currentTurnStatus: this.getCurrentTurnStatus(),
|
|
217
|
+
capabilities: this.getCapabilities(),
|
|
210
218
|
};
|
|
211
219
|
}
|
|
212
220
|
getSessionInfo() {
|
|
@@ -869,6 +877,106 @@ export class ClaudeAgentSdkSession extends EventEmitter {
|
|
|
869
877
|
}
|
|
870
878
|
}
|
|
871
879
|
}
|
|
880
|
+
/**
|
|
881
|
+
* Trigger Claude's native `/goal` slash command. Implemented by sending the
|
|
882
|
+
* prompt `"/goal <objective>"` through {@link runTurn}, which the Claude
|
|
883
|
+
* Agent SDK natively recognizes as a long-running goal request.
|
|
884
|
+
*
|
|
885
|
+
* Callers should detect support via `typeof session.runGoal === "function"`.
|
|
886
|
+
* When a provider does not support goals it should leave this method
|
|
887
|
+
* undefined; callers MUST surface a clear error rather than silently falling
|
|
888
|
+
* back to {@link runTurn}.
|
|
889
|
+
*
|
|
890
|
+
* Implementation note (N8): Claude's `/goal` slash command resolves the
|
|
891
|
+
* entire goal within a single `query()` iteration. {@link runTurn} drains
|
|
892
|
+
* the SDK iterator to completion (`for await (const message of query)`)
|
|
893
|
+
* before returning, so `turnResult.items` is the full, terminal sequence of
|
|
894
|
+
* messages. If the SDK emits multiple `goal_status` items (e.g. an
|
|
895
|
+
* intermediate "active" followed by a terminal "complete"), we select the
|
|
896
|
+
* LAST one rather than the first so the goal's final state is reflected.
|
|
897
|
+
* When no `goal_status` item is present we fall back to "active" and emit a
|
|
898
|
+
* debug log; that fallback is a signal that the contract has drifted.
|
|
899
|
+
*
|
|
900
|
+
* @param {import("../shared.js").GoalRequest} goal
|
|
901
|
+
* @param {object} [options]
|
|
902
|
+
* @returns {Promise<import("../shared.js").GoalResult>}
|
|
903
|
+
*/
|
|
904
|
+
async runGoal(goal, options = {}) {
|
|
905
|
+
const objective = typeof goal?.objective === "string" ? goal.objective.trim() : "";
|
|
906
|
+
if (!objective) {
|
|
907
|
+
throw createTurnError("runGoal requires a non-empty objective", {
|
|
908
|
+
reason: "invalid_goal",
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
const prompt = `/goal ${objective}`;
|
|
912
|
+
const turnResult = await this.runTurn(prompt, options);
|
|
913
|
+
const items = Array.isArray(turnResult?.items) ? turnResult.items : [];
|
|
914
|
+
// Walk from the end so we settle on the LAST goal_status item the SDK
|
|
915
|
+
// emitted. The single-shot `query()` iteration has been drained by
|
|
916
|
+
// `runTurn`, so this is the terminal status.
|
|
917
|
+
let goalStatusItem = null;
|
|
918
|
+
for (let idx = items.length - 1; idx >= 0; idx -= 1) {
|
|
919
|
+
const item = items[idx];
|
|
920
|
+
if (!item || typeof item !== "object") {
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
if (item.type === "goal_status" ||
|
|
924
|
+
item.subtype === "goal_status" ||
|
|
925
|
+
(item.goal_status && typeof item.goal_status === "object")) {
|
|
926
|
+
goalStatusItem = item;
|
|
927
|
+
break;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
const parsedStatus = (() => {
|
|
931
|
+
if (!goalStatusItem) {
|
|
932
|
+
return null;
|
|
933
|
+
}
|
|
934
|
+
const candidate = goalStatusItem.goal_status && typeof goalStatusItem.goal_status === "object"
|
|
935
|
+
? goalStatusItem.goal_status
|
|
936
|
+
: goalStatusItem;
|
|
937
|
+
const status = typeof candidate?.status === "string" ? candidate.status : null;
|
|
938
|
+
const id = typeof candidate?.id === "string" ? candidate.id : undefined;
|
|
939
|
+
const tokenBudget = candidate?.tokenBudget === null || Number.isFinite(Number(candidate?.tokenBudget))
|
|
940
|
+
? candidate.tokenBudget
|
|
941
|
+
: undefined;
|
|
942
|
+
return { status, id, tokenBudget };
|
|
943
|
+
})();
|
|
944
|
+
if (!goalStatusItem) {
|
|
945
|
+
// Contract drift: the SDK did not surface a goal_status item in the
|
|
946
|
+
// single query iteration. Log so operators notice; downstream we
|
|
947
|
+
// default to "active" so a still-running goal isn't misclassified as
|
|
948
|
+
// terminal.
|
|
949
|
+
this.trace(`runGoal: no goal_status item found in SDK message stream (${items.length} items); defaulting status to "active"`);
|
|
950
|
+
}
|
|
951
|
+
// Validate the SDK-reported status against the known GoalStatus enum
|
|
952
|
+
// before exposing it. An unknown / typo'd status (e.g. "weird") would
|
|
953
|
+
// otherwise leak through and confuse downstream goal-status consumers
|
|
954
|
+
// (the realtime hub uses isTerminalGoalStatus to decide when to stop
|
|
955
|
+
// polling). Default to "active" so a still-running goal is not
|
|
956
|
+
// misclassified as terminal.
|
|
957
|
+
const rawStatus = typeof parsedStatus?.status === "string" ? parsedStatus.status.trim() : "";
|
|
958
|
+
const validatedStatus = isGoalStatus(rawStatus) ? rawStatus : "active";
|
|
959
|
+
const goalState = {
|
|
960
|
+
id: parsedStatus?.id,
|
|
961
|
+
threadId: this.sessionId || undefined,
|
|
962
|
+
objective,
|
|
963
|
+
status: validatedStatus,
|
|
964
|
+
tokenBudget: goal?.tokenBudget !== undefined
|
|
965
|
+
? goal.tokenBudget
|
|
966
|
+
: parsedStatus?.tokenBudget !== undefined
|
|
967
|
+
? parsedStatus.tokenBudget
|
|
968
|
+
: null,
|
|
969
|
+
};
|
|
970
|
+
return {
|
|
971
|
+
text: turnResult?.text || "",
|
|
972
|
+
goal: goalState,
|
|
973
|
+
usage: turnResult?.usage || null,
|
|
974
|
+
metadata: {
|
|
975
|
+
...(turnResult?.metadata && typeof turnResult.metadata === "object" ? turnResult.metadata : {}),
|
|
976
|
+
goalPrompt: prompt,
|
|
977
|
+
},
|
|
978
|
+
};
|
|
979
|
+
}
|
|
872
980
|
async close() {
|
|
873
981
|
if (this.closed) {
|
|
874
982
|
return;
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
export class CodexAppServerSession extends EventEmitter<[never]> {
|
|
2
|
+
/**
|
|
3
|
+
* Optional feature flags surfaced to callers via {@link getSnapshot}.
|
|
4
|
+
* Treat as read-only.
|
|
5
|
+
* @type {Readonly<import("../shared.js").SessionCapabilities>}
|
|
6
|
+
*/
|
|
7
|
+
static capabilities: Readonly<import("../shared.js").SessionCapabilities>;
|
|
2
8
|
constructor(backend: any, options?: {});
|
|
9
|
+
/**
|
|
10
|
+
* @returns {import("../shared.js").SessionCapabilities}
|
|
11
|
+
*/
|
|
12
|
+
getCapabilities(): import("../shared.js").SessionCapabilities;
|
|
3
13
|
backend: string;
|
|
4
14
|
options: {};
|
|
5
15
|
logger: any;
|
|
@@ -32,9 +42,36 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
|
|
|
32
42
|
activeAssistantMessageText: string;
|
|
33
43
|
resolve: null;
|
|
34
44
|
reject: null;
|
|
45
|
+
} | {
|
|
46
|
+
turnId: string;
|
|
47
|
+
fullText: string;
|
|
48
|
+
activeAssistantMessageId: string;
|
|
49
|
+
activeAssistantMessageText: string;
|
|
50
|
+
resolve: () => void;
|
|
51
|
+
reject: (error: any) => void;
|
|
52
|
+
goalMode: boolean;
|
|
35
53
|
} | null;
|
|
36
54
|
bootPromise: Promise<void> | null;
|
|
37
55
|
booted: boolean;
|
|
56
|
+
goalMode: boolean;
|
|
57
|
+
currentGoal: (import("../shared.js").GoalState & {
|
|
58
|
+
raw?: unknown;
|
|
59
|
+
}) | null;
|
|
60
|
+
currentGoalRun: {
|
|
61
|
+
id: undefined;
|
|
62
|
+
threadId: any;
|
|
63
|
+
objective: string;
|
|
64
|
+
status: string;
|
|
65
|
+
tokenBudget: number | null;
|
|
66
|
+
fullText: string;
|
|
67
|
+
lastTurnText: string;
|
|
68
|
+
latestGoal: null;
|
|
69
|
+
completion: Promise<any>;
|
|
70
|
+
pendingDeltasByTurnId: Map<any, any>;
|
|
71
|
+
handleGoalUpdate: (payload: any) => void;
|
|
72
|
+
handleGoalCleared: (previous: any) => void;
|
|
73
|
+
handleTurnFailed: (error: any) => void;
|
|
74
|
+
} | null;
|
|
38
75
|
transport: CodexAppServerTransport;
|
|
39
76
|
writeLog(message: any): void;
|
|
40
77
|
trace(message: any): void;
|
|
@@ -56,6 +93,7 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
|
|
|
56
93
|
command: string;
|
|
57
94
|
} | null;
|
|
58
95
|
currentTurnStatus: any;
|
|
96
|
+
capabilities: import("../shared.js").SessionCapabilities;
|
|
59
97
|
pid: number | undefined;
|
|
60
98
|
};
|
|
61
99
|
getSessionInfo(): any;
|
|
@@ -109,6 +147,14 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
|
|
|
109
147
|
activeAssistantMessageText: string;
|
|
110
148
|
resolve: null;
|
|
111
149
|
reject: null;
|
|
150
|
+
} | {
|
|
151
|
+
turnId: string;
|
|
152
|
+
fullText: string;
|
|
153
|
+
activeAssistantMessageId: string;
|
|
154
|
+
activeAssistantMessageText: string;
|
|
155
|
+
resolve: () => void;
|
|
156
|
+
reject: (error: any) => void;
|
|
157
|
+
goalMode: boolean;
|
|
112
158
|
} | null, { messageId }?: {
|
|
113
159
|
messageId?: string | undefined;
|
|
114
160
|
}): Promise<boolean>;
|
|
@@ -133,6 +179,14 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
|
|
|
133
179
|
activeAssistantMessageText: string;
|
|
134
180
|
resolve: null;
|
|
135
181
|
reject: null;
|
|
182
|
+
} | {
|
|
183
|
+
turnId: string;
|
|
184
|
+
fullText: string;
|
|
185
|
+
activeAssistantMessageId: string;
|
|
186
|
+
activeAssistantMessageText: string;
|
|
187
|
+
resolve: () => void;
|
|
188
|
+
reject: (error: any) => void;
|
|
189
|
+
goalMode: boolean;
|
|
136
190
|
} | null;
|
|
137
191
|
ensureCurrentTurn(params: any): {
|
|
138
192
|
turnId: string;
|
|
@@ -141,6 +195,14 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
|
|
|
141
195
|
activeAssistantMessageText: string;
|
|
142
196
|
resolve: null;
|
|
143
197
|
reject: null;
|
|
198
|
+
} | {
|
|
199
|
+
turnId: string;
|
|
200
|
+
fullText: string;
|
|
201
|
+
activeAssistantMessageId: string;
|
|
202
|
+
activeAssistantMessageText: string;
|
|
203
|
+
resolve: () => void;
|
|
204
|
+
reject: (error: any) => void;
|
|
205
|
+
goalMode: boolean;
|
|
144
206
|
} | null;
|
|
145
207
|
queueAssistantDelta(delta: any, { messageId }?: {
|
|
146
208
|
messageId?: string | undefined;
|
|
@@ -173,6 +235,51 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
|
|
|
173
235
|
nativeSessionId: string | undefined;
|
|
174
236
|
};
|
|
175
237
|
}>;
|
|
238
|
+
/**
|
|
239
|
+
* Normalize a goal payload from a codex `thread/goal/*` notification into
|
|
240
|
+
* the cross-layer {@link GoalState} shape.
|
|
241
|
+
*
|
|
242
|
+
* @param {unknown} payload
|
|
243
|
+
* @returns {(import("../shared.js").GoalState & { raw?: unknown }) | null}
|
|
244
|
+
*/
|
|
245
|
+
normalizeGoalPayload(payload: unknown): (import("../shared.js").GoalState & {
|
|
246
|
+
raw?: unknown;
|
|
247
|
+
}) | null;
|
|
248
|
+
/**
|
|
249
|
+
* Trigger Codex's experimental `thread/goal/set` JSON-RPC. Requires the
|
|
250
|
+
* app-server to have been booted with `--enable goals`; dynamic enablement
|
|
251
|
+
* is NOT supported. Pass `{ goalMode: true }` to the session factory so the
|
|
252
|
+
* transport spawns with the right flag.
|
|
253
|
+
*
|
|
254
|
+
* Resolves when the goal reaches a terminal status (`complete`, `blocked`,
|
|
255
|
+
* `usageLimited`, `budgetLimited`) or when a `thread/goal/cleared`
|
|
256
|
+
* notification arrives.
|
|
257
|
+
*
|
|
258
|
+
* @param {import("../shared.js").GoalRequest} goal
|
|
259
|
+
* @param {object} [options]
|
|
260
|
+
* @returns {Promise<import("../shared.js").GoalResult>}
|
|
261
|
+
*/
|
|
262
|
+
runGoal(goal: import("../shared.js").GoalRequest, options?: object): Promise<import("../shared.js").GoalResult>;
|
|
263
|
+
/**
|
|
264
|
+
* Fetch the current goal via `thread/goal/get`.
|
|
265
|
+
*
|
|
266
|
+
* @returns {Promise<import("../shared.js").GoalState | null>}
|
|
267
|
+
*/
|
|
268
|
+
getGoal(): Promise<import("../shared.js").GoalState | null>;
|
|
269
|
+
/**
|
|
270
|
+
* Clear the current goal via `thread/goal/clear`.
|
|
271
|
+
*
|
|
272
|
+
* If a {@link runGoal} call is in flight when this is invoked externally,
|
|
273
|
+
* the pending `currentGoalRun.completion` is unblocked with a synthetic
|
|
274
|
+
* "cleared" payload so the caller's `await runGoal(...)` resolves promptly
|
|
275
|
+
* instead of waiting for the turn-timeout. The server-initiated
|
|
276
|
+
* `thread/goal/clear` does not emit a `thread/goal/cleared` notification
|
|
277
|
+
* (it's the response to OUR own RPC), so we have to wake the goal-run
|
|
278
|
+
* ourselves here.
|
|
279
|
+
*
|
|
280
|
+
* @returns {Promise<boolean>} true if a goal was cleared, false if there was nothing to clear.
|
|
281
|
+
*/
|
|
282
|
+
clearGoal(): Promise<boolean>;
|
|
176
283
|
close(): Promise<void>;
|
|
177
284
|
}
|
|
178
285
|
import { EventEmitter } from "node:events";
|