@love-moon/ai-sdk 0.13.3 → 0.14.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.
Files changed (33) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/client.d.ts +5 -0
  3. package/dist/client.js +22 -0
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +1 -0
  6. package/dist/providers/chat-web-session.d.ts +18 -0
  7. package/dist/providers/chat-web-session.js +44 -1
  8. package/dist/providers/claude-agent-sdk-session.d.ts +34 -0
  9. package/dist/providers/claude-agent-sdk-session.js +33 -0
  10. package/dist/providers/codex-app-server-session.d.ts +76 -6
  11. package/dist/providers/codex-app-server-session.js +113 -14
  12. package/dist/providers/codex-exec-session.d.ts +14 -0
  13. package/dist/providers/codex-exec-session.js +20 -1
  14. package/dist/providers/copilot-sdk-session.d.ts +21 -0
  15. package/dist/providers/copilot-sdk-session.js +55 -12
  16. package/dist/providers/dsh-sdk-session.d.ts +53 -3
  17. package/dist/providers/dsh-sdk-session.js +24 -1
  18. package/dist/providers/kimi-cli-session.d.ts +22 -0
  19. package/dist/providers/kimi-cli-session.js +23 -1
  20. package/dist/providers/kimi-print-session.d.ts +13 -0
  21. package/dist/providers/kimi-print-session.js +29 -0
  22. package/dist/providers/kimi-slash-commands.d.ts +1 -0
  23. package/dist/providers/kimi-slash-commands.js +23 -0
  24. package/dist/providers/opencode-sdk-session.d.ts +52 -3
  25. package/dist/providers/opencode-sdk-session.js +33 -1
  26. package/dist/shared.d.ts +46 -0
  27. package/dist/shared.js +18 -1
  28. package/dist/transports/kimi-wire-transport.d.ts +12 -0
  29. package/dist/transports/kimi-wire-transport.js +40 -1
  30. package/dist/turn-usage.d.ts +31 -0
  31. package/dist/turn-usage.js +44 -0
  32. package/dist/worker.js +1 -1
  33. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,60 @@
1
1
  # @love-moon/ai-sdk
2
2
 
3
+ ## 0.14.0
4
+
5
+ ### Minor Changes
6
+
7
+ - e168077: Support `/clear` in task chats. Fire detects a bare `/clear` message (like
8
+ `/compact`) and drops the AI's context instead of sending the text to the model.
9
+ Sessions advertise `capabilities.clear` and implement the new optional
10
+ `runClear()` on top of each backend's own native reset, so the backend process,
11
+ browser or RPC connection stays up:
12
+
13
+ - claude: native `/clear` slash command (the SDK's `conversation_reset` reports
14
+ the new conversation id)
15
+ - codex app-server: `thread/start` on the live transport
16
+ - codex exec: drops the replayed history, which is the whole context for a
17
+ stateless CLI
18
+ - copilot: `createSession()` on the same client; the old conversation is
19
+ detached, not deleted
20
+ - kimi wire and legacy print: the CLI's built-in `/clear` (prompt mode opts out)
21
+ - opencode: `session.create` on the same `opencode serve`
22
+ - dsh: rotates onto a fresh wire session id and keeps the runtime subprocess
23
+ - chat-web: `newChat()` in the same browser and profile
24
+
25
+ Backends without the capability (external providers) fall back to closing the
26
+ session and continuing on a brand-new one. Either way the task's chat history is
27
+ kept and the task is rebound to the session the backend ends up on, so a later
28
+ restart resumes the cleared conversation rather than the old one. The codex
29
+ fresh-session bootstrap lock now covers that fallback too, and the kimi wire
30
+ transport drops configured `--continue`/`--resume`/`--session` flags (like kimi
31
+ print already did) so they cannot reattach a cleared session to the old
32
+ conversation. Two caveats: the configured `pre_prompt` is not re-sent to the
33
+ cleared session, and a chat-web task running with
34
+ `CONDUCTOR_AI_SDK_DISABLE_WORKER=1` can only clear natively — its fallback would
35
+ hit the browser profile lock, because in that mode the lock owner is fire itself.
36
+
37
+ - 12a829c: Show each AI reply's token usage beside its timestamp: the turn's tokens, the
38
+ task total after it, and the input cache share (the part of the turn's input
39
+ served from the prompt cache).
40
+
41
+ - `@love-moon/ai-sdk`: new `summarizeTurnUsage(usage)` normalizes a turn's usage
42
+ into `{ tokens, inputTokens, cachedInputTokens }` (Claude: fresh input + cache
43
+ writes + cache reads; Codex: input already includes cached). The Codex
44
+ app-server provider's turn/goal `usage` now also carries `turnInputTokens` and
45
+ `turnCachedInputTokens`.
46
+ - `@love-moon/conductor-sdk`: `sendTurnUsage` accepts `input_tokens`,
47
+ `cached_input_tokens` and the reply's `message_id`.
48
+ - `conductor fire` reports a finished turn's usage after its reply, naming that
49
+ reply (for streamed sessions, the turn's last streamed reply), so the server
50
+ records it on the message (`metadata.turn_usage`) and pushes it to open chats.
51
+
52
+ ### Patch Changes
53
+
54
+ - d4f6fb9: Add opt-in Codex reply timing diagnostics to distinguish first-text generation
55
+ from buffering before a complete message is forwarded. Traces contain IDs and
56
+ monotonic durations, never prompt or reply text.
57
+
3
58
  ## 0.13.3
4
59
 
5
60
  ### Patch Changes
package/dist/client.d.ts CHANGED
@@ -19,6 +19,7 @@ export class RemoteAiSession extends EventEmitter<[never]> {
19
19
  capabilities: {
20
20
  goal?: boolean | undefined;
21
21
  compact?: boolean | undefined;
22
+ clear?: boolean | undefined;
22
23
  };
23
24
  };
24
25
  sessionMessageHandler: any;
@@ -53,6 +54,7 @@ export class RemoteAiSession extends EventEmitter<[never]> {
53
54
  capabilities: {
54
55
  goal?: boolean | undefined;
55
56
  compact?: boolean | undefined;
57
+ clear?: boolean | undefined;
56
58
  };
57
59
  };
58
60
  usesSessionFileReplyStream(): boolean;
@@ -72,6 +74,7 @@ export class RemoteAiSession extends EventEmitter<[never]> {
72
74
  runTurn(promptText: any, options?: {}): Promise<any>;
73
75
  runGoal(goal: any, options?: {}): Promise<any>;
74
76
  runCompact(request?: {}, options?: {}): Promise<any>;
77
+ runClear(request?: {}, options?: {}): Promise<any>;
75
78
  getGoal(): Promise<any>;
76
79
  clearGoal(): Promise<any>;
77
80
  close(): Promise<void>;
@@ -112,6 +115,7 @@ declare class LocalAiSessionProxy extends EventEmitter<[never]> {
112
115
  capabilities: {
113
116
  goal?: boolean | undefined;
114
117
  compact?: boolean | undefined;
118
+ clear?: boolean | undefined;
115
119
  };
116
120
  };
117
121
  readyPromise: Promise<any>;
@@ -131,6 +135,7 @@ declare class LocalAiSessionProxy extends EventEmitter<[never]> {
131
135
  runTurn(promptText: any, options?: {}): Promise<any>;
132
136
  runGoal(goal: any, options?: {}): Promise<any>;
133
137
  runCompact(request?: {}, options?: {}): Promise<any>;
138
+ runClear(request?: {}, options?: {}): Promise<any>;
134
139
  getGoal(): Promise<any>;
135
140
  clearGoal(): Promise<any>;
136
141
  close(): Promise<void>;
package/dist/client.js CHANGED
@@ -14,6 +14,11 @@ function createUnsupportedCompactError(name) {
14
14
  err.reason = "unsupported_compact";
15
15
  return err;
16
16
  }
17
+ function createUnsupportedClearError(name) {
18
+ const err = new Error(`runClear() is not supported by backend session (${name || "unknown"}).`);
19
+ err.reason = "unsupported_clear";
20
+ return err;
21
+ }
17
22
  const WORKER_PATH = fileURLToPath(new URL("./worker.js", import.meta.url));
18
23
  function sanitizeOptionsForWorker(options = {}) {
19
24
  const sanitized = {};
@@ -232,6 +237,16 @@ export class RemoteAiSession extends EventEmitter {
232
237
  progressHandler: typeof onProgress === "function" ? onProgress : null,
233
238
  });
234
239
  }
240
+ async runClear(request = {}, options = {}) {
241
+ await this.readyPromise;
242
+ if (!this.snapshot?.capabilities || this.snapshot.capabilities.clear !== true) {
243
+ throw createUnsupportedClearError(this.snapshot?.provider || this.snapshot?.backend);
244
+ }
245
+ const { onProgress, ...restOptions } = options || {};
246
+ return this.callWorker("runClear", [request, restOptions], {
247
+ progressHandler: typeof onProgress === "function" ? onProgress : null,
248
+ });
249
+ }
235
250
  async getGoal() {
236
251
  // Propagate worker-creation errors instead of masking them as "no goal".
237
252
  // See N3 in the review.
@@ -699,6 +714,13 @@ class LocalAiSessionProxy extends EventEmitter {
699
714
  }
700
715
  return await session.runCompact(request, options);
701
716
  }
717
+ async runClear(request = {}, options = {}) {
718
+ const session = await this.readyPromise;
719
+ if (this.snapshot?.capabilities?.clear !== true || typeof session.runClear !== "function") {
720
+ throw createUnsupportedClearError(session?.constructor?.name);
721
+ }
722
+ return await session.runClear(request, options);
723
+ }
702
724
  async getGoal() {
703
725
  const session = await this.readyPromise;
704
726
  const capabilities = resolveSessionCapabilities(session);
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { BUILT_IN_BACKENDS } from "./built-in-backends.js";
2
2
  export { PROVIDER_MEDIA_CAPABILITIES } from "./media-adapters.js";
3
+ export { summarizeTurnUsage } from "./turn-usage.js";
3
4
  export { createAiSession, RemoteAiSession } from "./client.js";
4
5
  export { isClaudeRootPermissionRestricted, resolveClaudeCommandForRoot, resolveClaudePermissionPolicy } from "./providers/claude-agent-sdk-session.js";
5
6
  export { appendContextFilesToPrompt, normalizeContextFiles } from "./context-files.js";
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ export { BUILT_IN_BACKENDS } from "./built-in-backends.js";
3
3
  export { PROVIDER_MEDIA_CAPABILITIES } from "./media-adapters.js";
4
4
  export { isClaudeRootPermissionRestricted, resolveClaudeCommandForRoot, resolveClaudePermissionPolicy, } from "./providers/claude-agent-sdk-session.js";
5
5
  export { appendContextFilesToPrompt, normalizeContextFiles } from "./context-files.js";
6
+ export { summarizeTurnUsage } from "./turn-usage.js";
6
7
  export { assertMediaCapabilities, fileMediaToBase64, fileMediaToDataUri, normalizeMediaInputs, resolveTurnMedia, } from "./media-input.js";
7
8
  export { GOAL_STATUSES, TERMINAL_GOAL_STATUSES, isGoalStatus, isTerminalGoalStatus, } from "./shared.js";
8
9
  export { AiManager, accountNameFromPath, checkInstall, checkInstallAll, checkNetwork, checkNetworkAll, getClaudeQuota, getCodexQuota, getCopilotQuota, getCurrentCodexAccount, getDshQuota, readCachedDshQuota, resolveDeepSeekCredential, getExternalQuota, getExternalQuotaList, getKimiQuota, listCodexAccounts, loadAiManagerConfig, normalizeExternalQuota, normalizeExternalQuotaList, parseAuthFile, parseAuthFileContents, parseCopilotQuotaSnapshots, readCachedCodexQuota, resolveClaudeCredential, switchCodexAccount, } from "./manager/index.js";
@@ -110,6 +110,7 @@ export class ChatWebSession extends EventEmitter<[never]> {
110
110
  updated_at: string;
111
111
  } | null;
112
112
  capabilities: {
113
+ clear: boolean;
113
114
  media: Readonly<{
114
115
  image: "unsupported";
115
116
  }>;
@@ -162,6 +163,23 @@ export class ChatWebSession extends EventEmitter<[never]> {
162
163
  getChatWebModule(): Promise<any>;
163
164
  boot(): Promise<void>;
164
165
  bootInternal(): Promise<void>;
166
+ /**
167
+ * Native clear: start a new conversation in the SAME browser and profile
168
+ * (chat-web clicks the provider's "new chat" control, falling back to
169
+ * navigating home — both land on an empty chat), so no Chromium relaunch and
170
+ * no re-login. The old conversation stays in the provider's account.
171
+ *
172
+ * The new `/c/{uuid}` only exists after the next reply, so we drop back to a
173
+ * synthetic id until then — `ensureSessionInfo()` reports `sessionIdDeferred`
174
+ * for exactly this window.
175
+ */
176
+ runClear(): Promise<{
177
+ clear: {
178
+ status: string;
179
+ };
180
+ usage: null;
181
+ metadata: {};
182
+ }>;
165
183
  /**
166
184
  * Adopt the provider-side conversation id (ChatGPT /c/{uuid} or
167
185
  * equivalent) as our canonical sessionId, update sessionInfo, and emit
@@ -212,7 +212,7 @@ export class ChatWebSession extends EventEmitter {
212
212
  ? { ready: true, command: this.providerConversationUrl() }
213
213
  : null,
214
214
  currentTurnStatus: this.getCurrentTurnStatus(),
215
- capabilities: { media: PROVIDER_MEDIA_CAPABILITIES[CHAT_WEB_SESSION_VARIANT] },
215
+ capabilities: { clear: true, media: PROVIDER_MEDIA_CAPABILITIES[CHAT_WEB_SESSION_VARIANT] },
216
216
  chatWebProvider: this.chatWebProvider,
217
217
  providerConversationId: this.providerConversationId,
218
218
  providerUrl: this.providerConversationUrl(),
@@ -355,6 +355,49 @@ export class ChatWebSession extends EventEmitter {
355
355
  this.trace(`session ready provider=${this.chatWebProvider} id=${this.sessionId}`);
356
356
  this.emit("session", this.getSessionInfo());
357
357
  }
358
+ /**
359
+ * Native clear: start a new conversation in the SAME browser and profile
360
+ * (chat-web clicks the provider's "new chat" control, falling back to
361
+ * navigating home — both land on an empty chat), so no Chromium relaunch and
362
+ * no re-login. The old conversation stays in the provider's account.
363
+ *
364
+ * The new `/c/{uuid}` only exists after the next reply, so we drop back to a
365
+ * synthetic id until then — `ensureSessionInfo()` reports `sessionIdDeferred`
366
+ * for exactly this window.
367
+ */
368
+ async runClear() {
369
+ if (this.closeRequested) {
370
+ throw this.createSessionClosedError();
371
+ }
372
+ if (this.currentTurn) {
373
+ const error = new Error("chat-web turn already in progress");
374
+ error.reason = "turn_already_running";
375
+ throw error;
376
+ }
377
+ // Nothing has been said yet — fire boots the browser at announce time, so
378
+ // "booted" alone does not mean there is a conversation to walk away from.
379
+ if (!this.providerConversationId && this.history.length === 0) {
380
+ return { clear: { status: "noop" }, usage: null, metadata: {} };
381
+ }
382
+ await this.boot();
383
+ if (typeof this.chatSession?.newChat !== "function") {
384
+ throw new Error(`chat-web provider "${this.chatWebProvider}" cannot start a new chat`);
385
+ }
386
+ await this.chatSession.newChat();
387
+ this.providerConversationId = undefined;
388
+ this.sessionId = `chat-web-${this.chatWebProvider}-${Date.now().toString(36)}`;
389
+ this.sessionInfo = {
390
+ ...(this.sessionInfo || {}),
391
+ backend: this.backend,
392
+ sessionId: this.sessionId,
393
+ providerConversationId: undefined,
394
+ providerUrl: undefined,
395
+ };
396
+ this.history = [];
397
+ this.emit("session", this.getSessionInfo());
398
+ // No id to report: the provider mints one on the next reply.
399
+ return { clear: { status: "cleared" }, usage: null, metadata: {} };
400
+ }
358
401
  /**
359
402
  * Adopt the provider-side conversation id (ChatGPT /c/{uuid} or
360
403
  * equivalent) as our canonical sessionId, update sessionInfo, and emit
@@ -10,6 +10,7 @@ export class ClaudeAgentSdkSession extends EventEmitter<[never]> {
10
10
  static capabilities: Readonly<{
11
11
  goal: true;
12
12
  compact: true;
13
+ clear: true;
13
14
  media: Readonly<{
14
15
  image: "native";
15
16
  }>;
@@ -18,6 +19,7 @@ export class ClaudeAgentSdkSession extends EventEmitter<[never]> {
18
19
  getCapabilities(): {
19
20
  goal: true;
20
21
  compact: true;
22
+ clear: true;
21
23
  media: Readonly<{
22
24
  image: "native";
23
25
  }>;
@@ -47,6 +49,7 @@ export class ClaudeAgentSdkSession extends EventEmitter<[never]> {
47
49
  suppressReply: boolean;
48
50
  compactMetadata: null;
49
51
  compactError: string;
52
+ conversationReset: string;
50
53
  emittedAssistantMessage: boolean;
51
54
  fullText: string;
52
55
  items: never[];
@@ -95,6 +98,7 @@ export class ClaudeAgentSdkSession extends EventEmitter<[never]> {
95
98
  capabilities: {
96
99
  goal: true;
97
100
  compact: true;
101
+ clear: true;
98
102
  media: Readonly<{
99
103
  image: "native";
100
104
  }>;
@@ -175,6 +179,7 @@ export class ClaudeAgentSdkSession extends EventEmitter<[never]> {
175
179
  metadata?: undefined;
176
180
  compactMetadata?: undefined;
177
181
  compactError?: undefined;
182
+ conversationReset?: undefined;
178
183
  } | {
179
184
  text: any;
180
185
  usage: any;
@@ -190,6 +195,7 @@ export class ClaudeAgentSdkSession extends EventEmitter<[never]> {
190
195
  };
191
196
  compactMetadata: undefined;
192
197
  compactError: string | undefined;
198
+ conversationReset: string | undefined;
193
199
  }>;
194
200
  /**
195
201
  * Trigger Claude's native `/goal` slash command. Implemented by sending the
@@ -228,6 +234,34 @@ export class ClaudeAgentSdkSession extends EventEmitter<[never]> {
228
234
  runCompact(request?: import("../shared.js").CompactRequest, { onProgress }?: {
229
235
  onProgress?: Function;
230
236
  }): Promise<import("../shared.js").CompactResult>;
237
+ /**
238
+ * Native `/clear`: the CLI handles the slash command itself and continues on
239
+ * a new session id, so the process stays up and only the context is dropped.
240
+ * A session id that did NOT move means the command was not handled natively.
241
+ */
242
+ runClear(request?: {}, { onProgress }?: {
243
+ onProgress?: null | undefined;
244
+ }): Promise<{
245
+ clear: {
246
+ status: string;
247
+ sessionId?: undefined;
248
+ };
249
+ usage: null;
250
+ metadata: {};
251
+ } | {
252
+ clear: {
253
+ status: string;
254
+ sessionId: any;
255
+ };
256
+ usage: any;
257
+ metadata: {
258
+ source: string;
259
+ sessionId: any;
260
+ totalCostUsd: number | undefined;
261
+ modelUsage: any;
262
+ structuredOutput: any;
263
+ } | undefined;
264
+ }>;
231
265
  close(): Promise<void>;
232
266
  }
233
267
  import { EventEmitter } from "node:events";
@@ -234,6 +234,7 @@ export class ClaudeAgentSdkSession extends EventEmitter {
234
234
  static capabilities = Object.freeze({
235
235
  goal: true,
236
236
  compact: true,
237
+ clear: true,
237
238
  media: PROVIDER_MEDIA_CAPABILITIES[CLAUDE_PROVIDER_VARIANT],
238
239
  });
239
240
  getCapabilities() {
@@ -835,6 +836,12 @@ export class ClaudeAgentSdkSession extends EventEmitter {
835
836
  }
836
837
  }
837
838
  return;
839
+ case "conversation_reset":
840
+ // Emitted by /clear (and other fresh-session flows): the CLI mounted a
841
+ // new transcript, so this is the authoritative "context cleared" signal.
842
+ currentTurn.conversationReset = normalizeText(message.new_conversation_id) || "";
843
+ this.updateSessionInfo(currentTurn.conversationReset);
844
+ return;
838
845
  case "rate_limit_event":
839
846
  this.updateSessionInfo(message.session_id || message.sessionId);
840
847
  this.rateLimitInfo =
@@ -911,6 +918,7 @@ export class ClaudeAgentSdkSession extends EventEmitter {
911
918
  suppressReply,
912
919
  compactMetadata: null,
913
920
  compactError: "",
921
+ conversationReset: "",
914
922
  emittedAssistantMessage: false,
915
923
  fullText: "",
916
924
  items: [],
@@ -1014,6 +1022,7 @@ export class ClaudeAgentSdkSession extends EventEmitter {
1014
1022
  },
1015
1023
  compactMetadata: currentTurn.compactMetadata || undefined,
1016
1024
  compactError: currentTurn.compactError || undefined,
1025
+ conversationReset: currentTurn.conversationReset || undefined,
1017
1026
  };
1018
1027
  }
1019
1028
  catch (error) {
@@ -1202,6 +1211,30 @@ export class ClaudeAgentSdkSession extends EventEmitter {
1202
1211
  metadata: turnResult.metadata,
1203
1212
  };
1204
1213
  }
1214
+ /**
1215
+ * Native `/clear`: the CLI handles the slash command itself and continues on
1216
+ * a new session id, so the process stays up and only the context is dropped.
1217
+ * A session id that did NOT move means the command was not handled natively.
1218
+ */
1219
+ async runClear(request = {}, { onProgress = null } = {}) {
1220
+ if (!this.sessionId || this.pendingHistorySeed) {
1221
+ return { clear: { status: "noop" }, usage: null, metadata: {} };
1222
+ }
1223
+ const previousSessionId = this.sessionId;
1224
+ const turnResult = await this.runTurn("/clear", { onProgress, suppressReply: true });
1225
+ const cleared = Boolean(turnResult.conversationReset || (this.sessionId && this.sessionId !== previousSessionId));
1226
+ if (cleared) {
1227
+ this.history = [];
1228
+ }
1229
+ return {
1230
+ clear: {
1231
+ status: cleared ? "cleared" : "noop",
1232
+ sessionId: cleared ? this.sessionId : undefined,
1233
+ },
1234
+ usage: turnResult.usage,
1235
+ metadata: turnResult.metadata,
1236
+ };
1237
+ }
1205
1238
  async close() {
1206
1239
  if (this.closed) {
1207
1240
  return;
@@ -13,6 +13,7 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
13
13
  backend: string;
14
14
  options: {};
15
15
  logger: any;
16
+ traceReplyLatency: boolean;
16
17
  cwd: any;
17
18
  resumeSessionId: any;
18
19
  sessionId: any;
@@ -31,13 +32,26 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
31
32
  nativeSessionId: string;
32
33
  rateLimits: any;
33
34
  tokenUsage: any;
34
- turnTokenBaseline: number | null;
35
+ turnTokenBaseline: {
36
+ totalTokens: number;
37
+ inputTokens: number;
38
+ cachedInputTokens: number;
39
+ } | {
40
+ totalTokens: number;
41
+ inputTokens?: undefined;
42
+ cachedInputTokens?: undefined;
43
+ } | {
44
+ inputTokens?: number | undefined;
45
+ cachedInputTokens?: number | undefined;
46
+ totalTokens: number;
47
+ } | null;
35
48
  turnTokensSeen: boolean;
36
49
  currentTurnStatus: any;
37
50
  currentTurnActivityAt: number;
38
51
  now: any;
39
52
  turnDeadlineMs: any;
40
53
  currentTurn: {
54
+ startedAt: number;
41
55
  turnId: string;
42
56
  fullText: string;
43
57
  activeAssistantMessageId: string;
@@ -132,6 +146,13 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
132
146
  markTurnStartedStatus(): void;
133
147
  failPendingTurnStart(error: any): Promise<void>;
134
148
  boot(): Promise<void>;
149
+ buildThreadParams(): {
150
+ cwd: any;
151
+ approvalPolicy: string;
152
+ sandbox: string;
153
+ personality: string;
154
+ ephemeral: boolean;
155
+ };
135
156
  bootInternal(): Promise<void>;
136
157
  applyThreadInfo(payload: any, { resumeReady }?: {
137
158
  resumeReady?: boolean | undefined;
@@ -140,10 +161,11 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
140
161
  /** Start counting a turn's tokens from the thread total known right now. */
141
162
  beginTurnTokenAccounting(): void;
142
163
  /**
143
- * Thread token usage plus `turnTotalTokens`: tokens spent since the current
144
- * turn (or goal) started. Codex only reports thread-cumulative totals, and
145
- * only when a response completes, so a turn that ended before any response
146
- * did has unknown usage.
164
+ * Thread token usage plus `turnTotalTokens` (and its `turnInputTokens` /
165
+ * `turnCachedInputTokens` split): tokens spent since the current turn (or
166
+ * goal) started. Codex only reports thread-cumulative totals, and only when
167
+ * a response completes, so a turn that ended before any response did has
168
+ * unknown usage.
147
169
  */
148
170
  snapshotTokenUsage(): any;
149
171
  resolveContextUsagePercent(): number | undefined;
@@ -188,6 +210,7 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
188
210
  }): Promise<void>;
189
211
  emitAssistantMessage(text: any): Promise<void>;
190
212
  finalizeActiveAssistantMessage(currentTurn?: {
213
+ startedAt: number;
191
214
  turnId: string;
192
215
  fullText: string;
193
216
  activeAssistantMessageId: string;
@@ -228,6 +251,7 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
228
251
  useInitialImages?: boolean | undefined;
229
252
  }): string;
230
253
  getCurrentTurn(): {
254
+ startedAt: number;
231
255
  turnId: string;
232
256
  fullText: string;
233
257
  activeAssistantMessageId: string;
@@ -252,12 +276,21 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
252
276
  goalMode: boolean;
253
277
  } | null;
254
278
  ensureCurrentTurn(params: any): {
279
+ startedAt: number;
255
280
  turnId: string;
256
281
  fullText: string;
257
282
  activeAssistantMessageId: string;
258
283
  activeAssistantMessageText: string;
259
284
  resolve: null;
260
285
  reject: null;
286
+ } | {
287
+ turnId: string;
288
+ fullText: string;
289
+ activeAssistantMessageId: string;
290
+ activeAssistantMessageText: string;
291
+ resolve: null;
292
+ reject: null;
293
+ suppressReply: boolean;
261
294
  } | {
262
295
  turnId: string;
263
296
  fullText: string;
@@ -287,7 +320,44 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
287
320
  * @param {import("../shared.js").CompactRequest} [request]
288
321
  * @returns {Promise<import("../shared.js").CompactResult>}
289
322
  */
290
- runCompact(request?: import("../shared.js").CompactRequest): Promise<import("../shared.js").CompactResult>;
323
+ /**
324
+ * Native clear: start a fresh thread on the SAME app-server connection. The
325
+ * process, transport and model config stay up; only the conversation is
326
+ * replaced, so the next turn runs against an empty thread.
327
+ */
328
+ runClear(): Promise<{
329
+ clear: {
330
+ status: string;
331
+ sessionId?: undefined;
332
+ };
333
+ usage: null;
334
+ metadata: {};
335
+ } | {
336
+ clear: {
337
+ status: string;
338
+ sessionId: any;
339
+ };
340
+ usage: null;
341
+ metadata: {};
342
+ }>;
343
+ runCompact(request?: {}): Promise<{
344
+ compact: {
345
+ status: string;
346
+ instructionsApplied: boolean;
347
+ };
348
+ usage: null;
349
+ metadata: {};
350
+ } | {
351
+ compact: {
352
+ status: string;
353
+ instructionsApplied: boolean;
354
+ };
355
+ usage: any;
356
+ metadata: {
357
+ source: string;
358
+ threadId: any;
359
+ };
360
+ }>;
291
361
  /**
292
362
  * Normalize a goal payload from a codex `thread/goal/*` notification into
293
363
  * the cross-layer {@link GoalState} shape.