@cjhyy/code-shell-core 0.7.0-beta.1 → 0.7.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 (57) hide show
  1. package/dist/cc-orchestrator/agent-adapter.d.ts +2 -0
  2. package/dist/cc-orchestrator/agent-adapter.js +4 -0
  3. package/dist/cc-orchestrator/codex-session-history.d.ts +14 -1
  4. package/dist/cc-orchestrator/codex-session-history.js +64 -4
  5. package/dist/cc-orchestrator/external-agent-changes.js +22 -5
  6. package/dist/cc-orchestrator/external-agent-driver.d.ts +1 -1
  7. package/dist/cc-orchestrator/external-agent-driver.js +202 -38
  8. package/dist/cc-orchestrator/session-history.d.ts +35 -0
  9. package/dist/cc-orchestrator/session-history.js +96 -13
  10. package/dist/credentials/access.d.ts +1 -0
  11. package/dist/credentials/access.js +2 -0
  12. package/dist/credentials/index.d.ts +2 -1
  13. package/dist/credentials/index.js +1 -0
  14. package/dist/credentials/oauth.d.ts +20 -0
  15. package/dist/credentials/oauth.js +114 -0
  16. package/dist/credentials/store.d.ts +1 -0
  17. package/dist/credentials/store.js +3 -1
  18. package/dist/credentials/types.d.ts +47 -1
  19. package/dist/engine/engine.d.ts +6 -2
  20. package/dist/engine/engine.js +159 -192
  21. package/dist/engine/goal.d.ts +17 -0
  22. package/dist/engine/goal.js +16 -6
  23. package/dist/engine/input-attachments.js +156 -13
  24. package/dist/engine/run-image-input.d.ts +22 -0
  25. package/dist/engine/run-image-input.js +195 -0
  26. package/dist/engine/steer-queue.d.ts +3 -1
  27. package/dist/engine/steer-queue.js +10 -2
  28. package/dist/engine/turn-loop.d.ts +30 -1
  29. package/dist/engine/turn-loop.js +112 -17
  30. package/dist/hooks/goal-stop-hook.d.ts +33 -1
  31. package/dist/hooks/goal-stop-hook.js +202 -34
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.js +2 -2
  34. package/dist/preset/index.js +14 -4
  35. package/dist/protocol/server.js +1 -1
  36. package/dist/protocol/types.d.ts +2 -0
  37. package/dist/session/session-manager.js +34 -1
  38. package/dist/tool-system/builtin/agent-notifications.d.ts +11 -4
  39. package/dist/tool-system/builtin/agent-notifications.js +19 -7
  40. package/dist/tool-system/builtin/background-jobs.d.ts +25 -5
  41. package/dist/tool-system/builtin/background-jobs.js +105 -7
  42. package/dist/tool-system/builtin/cron-list.definition.d.ts +3 -0
  43. package/dist/tool-system/builtin/cron-list.definition.js +6 -0
  44. package/dist/tool-system/builtin/cron.d.ts +1 -2
  45. package/dist/tool-system/builtin/cron.js +9 -7
  46. package/dist/tool-system/builtin/drive-claude-code.d.ts +7 -0
  47. package/dist/tool-system/builtin/drive-claude-code.js +307 -20
  48. package/dist/tool-system/builtin/index.js +15 -3
  49. package/dist/tool-system/builtin/sleep.d.ts +1 -2
  50. package/dist/tool-system/builtin/sleep.definition.d.ts +8 -0
  51. package/dist/tool-system/builtin/sleep.definition.js +28 -0
  52. package/dist/tool-system/builtin/sleep.js +1 -22
  53. package/dist/tool-system/context.d.ts +18 -0
  54. package/dist/tool-system/mcp-manager.d.ts +14 -2
  55. package/dist/tool-system/mcp-manager.js +56 -7
  56. package/dist/types.d.ts +23 -7
  57. package/package.json +1 -1
@@ -19,23 +19,28 @@ function toolsOf(content) {
19
19
  const inp = p.input ?? {};
20
20
  const summary = inp.command ?? inp.file_path ?? inp.path ?? inp.url ?? inp.pattern ?? inp.query ?? "";
21
21
  const args = inp && typeof inp === "object" && Object.keys(inp).length > 0 ? inp : undefined;
22
- out.push({ name: typeof p.name === "string" ? p.name : "tool", summary: String(summary).slice(0, 120), args });
22
+ out.push({
23
+ name: typeof p.name === "string" ? p.name : "tool",
24
+ summary: String(summary).slice(0, 120),
25
+ args,
26
+ });
23
27
  }
24
28
  }
25
29
  return out;
26
30
  }
27
- /** Read the last `limit` user/assistant messages from a claude session jsonl. */
28
- export function readRecentHistory(cwd, sessionId, limit, claudeHome = join(homedir(), ".claude")) {
29
- const file = join(claudeHome, "projects", encodeCwd(cwd), `${sessionId}.jsonl`);
30
- if (!existsSync(file))
31
- return { messages: [], hasMore: false, totalCount: 0 };
32
- let raw;
33
- try {
34
- raw = readFileSync(file, "utf-8");
35
- }
36
- catch {
37
- return { messages: [], hasMore: false, totalCount: 0 };
38
- }
31
+ function toolResultText(content) {
32
+ if (typeof content === "string")
33
+ return content;
34
+ if (!Array.isArray(content))
35
+ return "";
36
+ return content
37
+ .map((part) => typeof part === "string" ? part : typeof part?.text === "string" ? part.text : "")
38
+ .join("");
39
+ }
40
+ /** Parse a bounded/raw Claude Code transcript snapshot. Exported so a live
41
+ * follower can take its initial snapshot and EOF cursor from the same read,
42
+ * eliminating the snapshot→subscribe race. */
43
+ export function parseRecentHistory(raw, limit) {
39
44
  const all = [];
40
45
  for (const line of raw.split("\n")) {
41
46
  if (!line.trim())
@@ -65,3 +70,81 @@ export function readRecentHistory(cwd, sessionId, limit, claudeHome = join(homed
65
70
  const start = Math.max(0, all.length - lim);
66
71
  return { messages: all.slice(start), hasMore: start > 0, totalCount: all.length };
67
72
  }
73
+ /** Parse one newly-appended Claude Code transcript JSONL line into the compact
74
+ * event vocabulary consumed by the desktop room follower. */
75
+ export function parseClaudeTranscriptLine(line) {
76
+ let d;
77
+ try {
78
+ d = JSON.parse(line);
79
+ }
80
+ catch {
81
+ return [];
82
+ }
83
+ const out = [];
84
+ if (d.type === "user") {
85
+ const content = d.message?.content;
86
+ if (Array.isArray(content)) {
87
+ for (const part of content) {
88
+ if (part?.type !== "tool_result")
89
+ continue;
90
+ out.push({
91
+ type: "tool_result",
92
+ id: typeof part.tool_use_id === "string" ? part.tool_use_id : undefined,
93
+ result: toolResultText(part.content).slice(0, 4000),
94
+ isError: Boolean(part.is_error),
95
+ });
96
+ }
97
+ }
98
+ const text = textOf(content).trim();
99
+ if (text && !NOISE.some((noise) => text.startsWith(noise))) {
100
+ out.unshift({ type: "user", text });
101
+ }
102
+ return out;
103
+ }
104
+ if (d.type === "assistant" && Array.isArray(d.message?.content)) {
105
+ for (const part of d.message.content) {
106
+ if (part?.type === "text" && typeof part.text === "string" && part.text) {
107
+ out.push({ type: "assistant", text: part.text });
108
+ }
109
+ else if (part?.type === "tool_use") {
110
+ const input = part.input && typeof part.input === "object"
111
+ ? part.input
112
+ : undefined;
113
+ out.push({
114
+ type: "tool",
115
+ id: typeof part.id === "string" ? part.id : undefined,
116
+ name: typeof part.name === "string" ? part.name : "tool",
117
+ summary: toolsOf([part])[0]?.summary ?? "",
118
+ args: input,
119
+ });
120
+ }
121
+ }
122
+ if (d.message.stop_reason === "end_turn") {
123
+ out.push({ type: "turn_end", reason: "completed" });
124
+ }
125
+ return out;
126
+ }
127
+ if (d.type === "result") {
128
+ return [
129
+ {
130
+ type: "turn_end",
131
+ reason: typeof d.subtype === "string" ? d.subtype : "completed",
132
+ },
133
+ ];
134
+ }
135
+ return [];
136
+ }
137
+ /** Read the last `limit` user/assistant messages from a claude session jsonl. */
138
+ export function readRecentHistory(cwd, sessionId, limit, claudeHome = join(homedir(), ".claude")) {
139
+ const file = join(claudeHome, "projects", encodeCwd(cwd), `${sessionId}.jsonl`);
140
+ if (!existsSync(file))
141
+ return { messages: [], hasMore: false, totalCount: 0 };
142
+ let raw;
143
+ try {
144
+ raw = readFileSync(file, "utf-8");
145
+ }
146
+ catch {
147
+ return { messages: [], hasMore: false, totalCount: 0 };
148
+ }
149
+ return parseRecentHistory(raw, limit);
150
+ }
@@ -12,6 +12,7 @@ export interface CredentialMetadata {
12
12
  meta?: Credential["meta"];
13
13
  hasSecret: boolean;
14
14
  secretHint?: string;
15
+ oauthStatus?: import("./types.js").OAuthCredentialPublicStatus;
15
16
  }
16
17
  export interface CredentialAccess {
17
18
  listMasked(cwd: string | undefined, scope: CredentialAccessScope): CredentialMetadata[];
@@ -4,6 +4,7 @@ import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { CredentialStore } from "./store.js";
6
6
  import { formatNetscapeCookies, parseCookieJar } from "./cookie-jar.js";
7
+ import { summarizeOAuthCredentialSecret } from "./oauth.js";
7
8
  const COOKIE_FILE_PREFIX = "codeshell-cred-cookie-";
8
9
  const COOKIE_FILE_MAX_AGE_MS = 30 * 60 * 1000;
9
10
  let defaultCredentialAccess = null;
@@ -107,6 +108,7 @@ function toMetadata(cred) {
107
108
  ...rest,
108
109
  hasSecret: available,
109
110
  secretHint: available ? (secret.length > 4 ? `****${secret.slice(-4)}` : "****") : undefined,
111
+ ...(cred.type === "oauth" ? { oauthStatus: summarizeOAuthCredentialSecret(secret) } : {}),
110
112
  };
111
113
  }
112
114
  function cloneMetadata(list) {
@@ -2,7 +2,8 @@ export { CredentialStore } from "./store.js";
2
2
  export type { CredentialScope, MaskedCredential } from "./store.js";
3
3
  export { type EncryptionCipher, PlaintextCipher, setDefaultCredentialCipher, getDefaultCredentialCipher, } from "./cipher.js";
4
4
  export { getCredentialAccess, setDefaultCredentialAccess, createIpcCredentialAccess, localCredentialAccess, credentialAccessScope, isCredentialSecretAvailable, materializeCookieSecret, type CredentialAccess, type CredentialAccessScope, type CredentialMetadata, type CredentialSnapshot, type CredentialSnapshotEntry, } from "./access.js";
5
- export type { Credential, CredentialType, CredentialStoreFile } from "./types.js";
5
+ export type { Credential, CredentialType, CredentialStoreFile, OAuthCredentialPublicStatus, OAuthCredentialSecret, } from "./types.js";
6
+ export { buildOAuthRefreshRequest, isOAuthAccessTokenExpired, oauthCredentialStatus, parseOAuthCredentialSecret, summarizeOAuthCredentialSecret, type OAuthClockOptions, type OAuthRefreshHandler, type OAuthRefreshRequest, } from "./oauth.js";
6
7
  export { formatNetscapeCookies, parseCookieJar, type CookieLike } from "./cookie-jar.js";
7
8
  export { useCredentialToolDef, useCredentialToolDefFor, useCredentialTool, sweepStaleCredentialCookies, } from "./use-credential-tool.js";
8
9
  export { credentialUseGate } from "./use-gate.js";
@@ -1,6 +1,7 @@
1
1
  export { CredentialStore } from "./store.js";
2
2
  export { PlaintextCipher, setDefaultCredentialCipher, getDefaultCredentialCipher, } from "./cipher.js";
3
3
  export { getCredentialAccess, setDefaultCredentialAccess, createIpcCredentialAccess, localCredentialAccess, credentialAccessScope, isCredentialSecretAvailable, materializeCookieSecret, } from "./access.js";
4
+ export { buildOAuthRefreshRequest, isOAuthAccessTokenExpired, oauthCredentialStatus, parseOAuthCredentialSecret, summarizeOAuthCredentialSecret, } from "./oauth.js";
4
5
  export { formatNetscapeCookies, parseCookieJar } from "./cookie-jar.js";
5
6
  export { useCredentialToolDef, useCredentialToolDefFor, useCredentialTool, sweepStaleCredentialCookies, } from "./use-credential-tool.js";
6
7
  export { credentialUseGate } from "./use-gate.js";
@@ -0,0 +1,20 @@
1
+ import type { OAuthCredentialPublicStatus, OAuthCredentialSecret } from "./types.js";
2
+ export interface OAuthClockOptions {
3
+ now?: number | (() => number);
4
+ skewMs?: number;
5
+ }
6
+ export interface OAuthRefreshRequest {
7
+ credentialId: string;
8
+ tokenEndpoint: string;
9
+ refreshToken: string;
10
+ clientId?: string;
11
+ clientSecret?: string;
12
+ scope?: string;
13
+ scopes?: string[];
14
+ }
15
+ export type OAuthRefreshHandler = (req: OAuthRefreshRequest) => Promise<OAuthCredentialSecret>;
16
+ export declare function parseOAuthCredentialSecret(secret: string): OAuthCredentialSecret;
17
+ export declare function isOAuthAccessTokenExpired(secret: Pick<OAuthCredentialSecret, "accessToken" | "expiresAt">, opts?: OAuthClockOptions): boolean;
18
+ export declare function oauthCredentialStatus(secret: Pick<OAuthCredentialSecret, "accessToken" | "expiresAt">, opts?: OAuthClockOptions): Pick<OAuthCredentialPublicStatus, "state" | "expiresAt" | "expiresInMs">;
19
+ export declare function buildOAuthRefreshRequest(credentialId: string, secret: OAuthCredentialSecret): OAuthRefreshRequest | undefined;
20
+ export declare function summarizeOAuthCredentialSecret(secret: string | undefined, opts?: OAuthClockOptions): OAuthCredentialPublicStatus;
@@ -0,0 +1,114 @@
1
+ const DEFAULT_OAUTH_REFRESH_SKEW_MS = 60_000;
2
+ function nowMs(opts = {}) {
3
+ if (typeof opts.now === "function")
4
+ return opts.now();
5
+ if (typeof opts.now === "number")
6
+ return opts.now;
7
+ return Date.now();
8
+ }
9
+ function optionalString(value) {
10
+ return typeof value === "string" && value.length > 0 ? value : undefined;
11
+ }
12
+ function optionalStringArray(value) {
13
+ if (!Array.isArray(value))
14
+ return undefined;
15
+ const out = value.filter((x) => typeof x === "string" && x.length > 0);
16
+ return out.length > 0 ? out : undefined;
17
+ }
18
+ function parseExpiresAt(value) {
19
+ if (!value)
20
+ return undefined;
21
+ const ms = Date.parse(value);
22
+ if (!Number.isFinite(ms)) {
23
+ throw new Error(`OAuth credential secret has invalid expiresAt: ${value}`);
24
+ }
25
+ return ms;
26
+ }
27
+ export function parseOAuthCredentialSecret(secret) {
28
+ let parsed;
29
+ try {
30
+ parsed = JSON.parse(secret);
31
+ }
32
+ catch {
33
+ throw new Error("OAuth credential secret must be a JSON object");
34
+ }
35
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
36
+ throw new Error("OAuth credential secret must be a JSON object");
37
+ }
38
+ const raw = parsed;
39
+ if (raw.version !== undefined && raw.version !== 1) {
40
+ throw new Error("OAuth credential secret version must be 1");
41
+ }
42
+ const accessToken = optionalString(raw.accessToken);
43
+ if (!accessToken) {
44
+ throw new Error("OAuth credential secret must include accessToken");
45
+ }
46
+ const expiresAt = optionalString(raw.expiresAt);
47
+ parseExpiresAt(expiresAt);
48
+ return {
49
+ version: raw.version === 1 ? 1 : undefined,
50
+ accessToken,
51
+ refreshToken: optionalString(raw.refreshToken),
52
+ expiresAt,
53
+ tokenType: optionalString(raw.tokenType),
54
+ scope: optionalString(raw.scope),
55
+ scopes: optionalStringArray(raw.scopes),
56
+ tokenEndpoint: optionalString(raw.tokenEndpoint),
57
+ clientId: optionalString(raw.clientId),
58
+ clientSecret: optionalString(raw.clientSecret),
59
+ };
60
+ }
61
+ export function isOAuthAccessTokenExpired(secret, opts = {}) {
62
+ const expiresAtMs = parseExpiresAt(secret.expiresAt);
63
+ if (expiresAtMs === undefined)
64
+ return false;
65
+ const skewMs = opts.skewMs ?? DEFAULT_OAUTH_REFRESH_SKEW_MS;
66
+ return expiresAtMs <= nowMs(opts) + skewMs;
67
+ }
68
+ export function oauthCredentialStatus(secret, opts = {}) {
69
+ const expiresAtMs = parseExpiresAt(secret.expiresAt);
70
+ if (expiresAtMs === undefined) {
71
+ return { state: "valid", expiresAt: undefined, expiresInMs: undefined };
72
+ }
73
+ const expiresInMs = expiresAtMs - nowMs(opts);
74
+ return {
75
+ state: isOAuthAccessTokenExpired(secret, opts) ? "expired" : "valid",
76
+ expiresAt: secret.expiresAt,
77
+ expiresInMs,
78
+ };
79
+ }
80
+ export function buildOAuthRefreshRequest(credentialId, secret) {
81
+ if (!secret.refreshToken || !secret.tokenEndpoint)
82
+ return undefined;
83
+ return {
84
+ credentialId,
85
+ tokenEndpoint: secret.tokenEndpoint,
86
+ refreshToken: secret.refreshToken,
87
+ clientId: secret.clientId,
88
+ clientSecret: secret.clientSecret,
89
+ scope: secret.scope,
90
+ scopes: secret.scopes,
91
+ };
92
+ }
93
+ export function summarizeOAuthCredentialSecret(secret, opts = {}) {
94
+ if (!secret)
95
+ return { state: "missing" };
96
+ try {
97
+ const parsed = parseOAuthCredentialSecret(secret);
98
+ const status = oauthCredentialStatus(parsed, opts);
99
+ return {
100
+ ...status,
101
+ hasRefreshToken: Boolean(parsed.refreshToken),
102
+ tokenEndpoint: parsed.tokenEndpoint,
103
+ clientId: parsed.clientId,
104
+ scope: parsed.scope,
105
+ scopes: parsed.scopes,
106
+ };
107
+ }
108
+ catch (err) {
109
+ return {
110
+ state: "invalid",
111
+ error: err instanceof Error ? err.message : String(err),
112
+ };
113
+ }
114
+ }
@@ -5,6 +5,7 @@ export interface MaskedCredential extends Omit<Credential, "secret"> {
5
5
  hasSecret: boolean;
6
6
  /** 形如 `****abcd`,绝不含完整明文。 */
7
7
  secretHint?: string;
8
+ oauthStatus?: import("./types.js").OAuthCredentialPublicStatus;
8
9
  }
9
10
  /**
10
11
  * 两层凭证库,镜像 SettingsManager 的 user(~/.code-shell)/ project(<cwd>/.code-shell)
@@ -1,8 +1,9 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync, } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from "node:fs";
2
2
  import { join, dirname } from "node:path";
3
3
  import { homedir } from "node:os";
4
4
  import { getDefaultCredentialCipher } from "./cipher.js";
5
5
  import { logger } from "../logging/logger.js";
6
+ import { summarizeOAuthCredentialSecret } from "./oauth.js";
6
7
  /** 测试可经 process.env.HOME 覆盖(镜像 settings/manager.ts userHome)。 */
7
8
  function userHome() {
8
9
  return process.env.HOME ?? homedir();
@@ -178,6 +179,7 @@ export class CredentialStore {
178
179
  // than 4, so those chars aren't the whole secret. `"ab".slice(-4)` is
179
180
  // "ab", so a short secret would otherwise leak in full through the hint.
180
181
  secretHint: secret ? (secret.length > 4 ? `****${secret.slice(-4)}` : "****") : undefined,
182
+ ...(c.type === "oauth" ? { oauthStatus: summarizeOAuthCredentialSecret(secret) } : {}),
181
183
  };
182
184
  });
183
185
  }
@@ -3,7 +3,40 @@
3
3
  * - token/link 第一期已有;cookie 第二期改为「具名 cookie 凭证」进库
4
4
  * (用户主动按域拓取存,支持同域多账号),见 credential-use-gate 设计稿。
5
5
  */
6
- export type CredentialType = "token" | "link" | "cookie";
6
+ export type CredentialType = "token" | "link" | "cookie" | "oauth";
7
+ export interface OAuthCredentialSecret {
8
+ /** Schema version for the JSON stored in Credential.secret. */
9
+ version?: 1;
10
+ /** Current access token. MCP HTTP auth sends this as Authorization: Bearer. */
11
+ accessToken: string;
12
+ /** Optional refresh token. Stored encrypted with the credential secret. */
13
+ refreshToken?: string;
14
+ /** ISO-8601 expiry time for accessToken. Missing means "unknown", not expired. */
15
+ expiresAt?: string;
16
+ /** Upstream token type, usually Bearer. Stored for round-trip/debugging. */
17
+ tokenType?: string;
18
+ /** Space-delimited OAuth scope string returned by many token endpoints. */
19
+ scope?: string;
20
+ /** Structured scopes used by first-party UI/catalog metadata. */
21
+ scopes?: string[];
22
+ /** Token endpoint to use when a future refresh flow is wired. */
23
+ tokenEndpoint?: string;
24
+ /** OAuth client id associated with this token. */
25
+ clientId?: string;
26
+ /** Optional confidential client secret, if the integration requires it. */
27
+ clientSecret?: string;
28
+ }
29
+ export interface OAuthCredentialPublicStatus {
30
+ state: "valid" | "expired" | "missing" | "invalid";
31
+ expiresAt?: string;
32
+ expiresInMs?: number;
33
+ hasRefreshToken?: boolean;
34
+ tokenEndpoint?: string;
35
+ clientId?: string;
36
+ scope?: string;
37
+ scopes?: string[];
38
+ error?: string;
39
+ }
7
40
  export interface Credential {
8
41
  /**
9
42
  * 引用键,kebab-case,全局/项目两层内唯一。
@@ -19,6 +52,7 @@ export interface Credential {
19
52
  * 密文(UI 只显示掩码):
20
53
  * - token: token 值;
21
54
  * - link: client id/secret 等的 JSON 字符串;
55
+ * - oauth: OAuthCredentialSecret JSON 字符串(access/refresh/expiry/client info);
22
56
  * - cookie: 序列化的 cookie jar(JSON.stringify 的 ElectronCookieLike[])。
23
57
  */
24
58
  secret?: string;
@@ -48,6 +82,18 @@ export interface Credential {
48
82
  domain?: string;
49
83
  scope?: "domain" | "all";
50
84
  switchMode?: "clear" | "merge";
85
+ /** OAuth provider/integration id, e.g. "figma". */
86
+ oauthProvider?: string;
87
+ /** OAuth authorization endpoint, used by UI/login launchers. */
88
+ authUrl?: string;
89
+ /** OAuth token endpoint, duplicated from secret for non-secret UI display. */
90
+ tokenEndpoint?: string;
91
+ /** OAuth client id, duplicated from secret for non-secret UI display. */
92
+ clientId?: string;
93
+ /** OAuth scopes requested for this credential. */
94
+ scopes?: string[];
95
+ /** Last successful refresh time; refresh wiring is reserved for a later step. */
96
+ lastRefreshAt?: string;
51
97
  };
52
98
  }
53
99
  export interface CredentialStoreFile {
@@ -154,6 +154,7 @@ export declare class Engine {
154
154
  * LLM response arrives.
155
155
  */
156
156
  private ctxOverheadBySid;
157
+ private lastCacheReadBySid;
157
158
  /**
158
159
  * Step-gap steering queue (per sessionId, in-memory). Host pushes user
159
160
  * messages here via enqueueSteer while a run is in flight; the turn loop
@@ -278,7 +279,7 @@ export declare class Engine {
278
279
  * the queued draft) and is the handle `unsteer` uses to revoke a still-pending
279
280
  * entry. A blank id is tolerated but means the entry can't be revoked.
280
281
  */
281
- enqueueSteer(sessionId: string, text: string, id?: string, clientMessageId?: string): EnqueueSteerResult;
282
+ enqueueSteer(sessionId: string, text: string, id?: string, clientMessageId?: string, attachments?: InputAttachmentMeta[]): EnqueueSteerResult;
282
283
  /**
283
284
  * Revoke a still-pending steer entry (the 撤回 path). Returns true if it was
284
285
  * removed, false if it was already consumed by the turn loop (can't take it
@@ -287,6 +288,8 @@ export declare class Engine {
287
288
  unsteer(sessionId: string, id: string): boolean;
288
289
  /** Drain + clear the steer queue for a session (turn loop consumes per step). */
289
290
  private consumeSteer;
291
+ /** Put failed steer preparation back ahead of messages queued while it was being prepared. */
292
+ private restoreSteer;
290
293
  /** Wire the cookie→browser injection callback (InjectCredential tool). Same
291
294
  * post-construction injection model as setBrowserBridge. */
292
295
  setInjectCredential(fn: import("../tool-system/context.js").InjectCredentialFn | undefined): void;
@@ -482,7 +485,8 @@ export declare class Engine {
482
485
  after: number;
483
486
  strategy: "none (no active session)" | "no compaction needed" | "compacted" | CompactStrategy;
484
487
  }>;
485
- private stripUserContextMessage;
488
+ private stripInjectedContextMessages;
489
+ private recordCacheReadDiagnostics;
486
490
  private getSettingsManager;
487
491
  /**
488
492
  * Update a config setting at runtime.