@keyou007/agent-bot 0.1.20-alpha.0 → 0.1.20

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 (66) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +18 -1
  3. package/README.zh.md +9 -0
  4. package/dist/cli/AgentPrerequisites.js +11 -47
  5. package/dist/cli/AgentPrerequisites.js.map +1 -1
  6. package/dist/cli/DefaultAgentSelection.js +1 -1
  7. package/dist/cli/DefaultAgentSelection.js.map +1 -1
  8. package/dist/cli.js +15 -0
  9. package/dist/cli.js.map +1 -1
  10. package/dist/codex/CodexLocalActivityDetector.js +32 -10
  11. package/dist/codex/CodexLocalActivityDetector.js.map +1 -1
  12. package/dist/codex/CodexProcessManager.js +53 -7
  13. package/dist/codex/CodexProcessManager.js.map +1 -1
  14. package/dist/codex/CodexRuntime.js +225 -164
  15. package/dist/codex/CodexRuntime.js.map +1 -1
  16. package/dist/codex/CodexVersion.js +18 -0
  17. package/dist/codex/CodexVersion.js.map +1 -0
  18. package/dist/codex/ProviderSwitch.js +32 -0
  19. package/dist/codex/ProviderSwitch.js.map +1 -0
  20. package/dist/config/AgentExecutionDefaultsStore.js +1 -1
  21. package/dist/config/AgentExecutionDefaultsStore.js.map +1 -1
  22. package/dist/feishu/CardRenderer.js +36 -11
  23. package/dist/feishu/CardRenderer.js.map +1 -1
  24. package/dist/feishu/FeishuConnector.js +17 -5
  25. package/dist/feishu/FeishuConnector.js.map +1 -1
  26. package/dist/feishu/InboundText.js +11 -0
  27. package/dist/feishu/InboundText.js.map +1 -1
  28. package/dist/feishu/MergedForwardMessage.js +17 -5
  29. package/dist/feishu/MergedForwardMessage.js.map +1 -1
  30. package/dist/index.js +1 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/local-files/LocalFileViewerServer.js +25 -7
  33. package/dist/local-files/LocalFileViewerServer.js.map +1 -1
  34. package/dist/local-files/MarkdownFileLinks.js +65 -0
  35. package/dist/local-files/MarkdownFileLinks.js.map +1 -0
  36. package/dist/proxy/ProxySessionController.js +400 -259
  37. package/dist/proxy/ProxySessionController.js.map +1 -1
  38. package/dist/state/StateStore.js +99 -65
  39. package/dist/state/StateStore.js.map +1 -1
  40. package/dist/utils/semanticVersion.js +39 -0
  41. package/dist/utils/semanticVersion.js.map +1 -0
  42. package/docs/technical-reference.md +5 -3
  43. package/docs/technical-reference.zh.md +5 -3
  44. package/npm-shrinkwrap.json +2 -2
  45. package/package.json +1 -1
  46. package/skills/agent-bot/SKILL.md +7 -1
  47. package/src/cli/AgentPrerequisites.ts +14 -40
  48. package/src/cli/DefaultAgentSelection.ts +1 -1
  49. package/src/cli.ts +13 -0
  50. package/src/codex/CodexLocalActivityDetector.ts +41 -9
  51. package/src/codex/CodexProcessManager.ts +44 -7
  52. package/src/codex/CodexRuntime.ts +244 -195
  53. package/src/codex/CodexVersion.ts +18 -0
  54. package/src/codex/ProviderSwitch.ts +36 -0
  55. package/src/config/AgentExecutionDefaultsStore.ts +1 -1
  56. package/src/feishu/CardRenderer.ts +37 -27
  57. package/src/feishu/FeishuConnector.ts +15 -5
  58. package/src/feishu/InboundText.ts +11 -0
  59. package/src/feishu/MergedForwardMessage.ts +15 -5
  60. package/src/index.ts +1 -1
  61. package/src/local-files/LocalFileViewerServer.ts +26 -7
  62. package/src/local-files/MarkdownFileLinks.ts +64 -0
  63. package/src/proxy/ProxySessionController.ts +370 -240
  64. package/src/runtime/types.ts +15 -2
  65. package/src/state/StateStore.ts +117 -90
  66. package/src/utils/semanticVersion.ts +28 -0
@@ -1 +1 @@
1
- {"version":3,"file":"CodexProcessManager.js","sourceRoot":"","sources":["../../src/codex/CodexProcessManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,QAAQ,MAAM,eAAe,CAAC;AAErC,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,OAAO,EACL,mBAAmB,GAEpB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,MAAM,OAAO,mBAAmB;IAQX,OAAO;IACP,IAAI;IACJ,GAAG;IACH,MAAM;IACN,kBAAkB;IAX7B,MAAM,CAAU,kBAAkB,GAAG,KAAK,CAAC;IAC3C,MAAM,CAAuB;IAC7B,KAAK,CAAkC;IACvC,OAAO,CAAU;IACR,mBAAmB,GAAG,IAAI,GAAG,EAA0B,CAAC;IAEzE,YACmB,OAAe,EACf,IAAc,EACd,GAA2B,EAC3B,MAAc,EACd,kBAAkB,GAAkC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;uBAJ9D,OAAO;oBACP,IAAI;mBACJ,GAAG;sBACH,MAAM;kCACN,kBAAkB;IAClC,CAAC;IAEJ,KAAK,CAAC,SAAS;QACb,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC;QACpC,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,MAAM,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACrD,MAAM,KAAK,GAAG,iBAAiB,CAC7B,IAAI,CAAC,OAAO,EACZ,IAAI,CAAC,IAAI,EACT,mBAAmB,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC,EAC9D,kBAAkB,CAAC,WAAW,IAAI,EAAE,CAAC,OAAO,EAAE,CAC/C,CAAC;QACF,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,MAAM,MAAM,GAAG,IAAI,mBAAmB,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC;QACpG,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACzF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,oBAAoB,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,2BAA2B,CAAC,CAAC,CAAC;QAC1F,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YAClC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,4BAA4B,CAAC,CAAC;YACjE,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM;gBAAE,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;YACpD,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK;gBAAE,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;YACjD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,2BAA2B,IAAI,IAAI,MAAM,YAAY,MAAM,IAAI,MAAM,IAAI,CAAC,CAAC;YACnG,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,mBAAmB;gBAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;QACH,MAAM,gBAAgB,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE;YAC1D,UAAU,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE;YACvE,YAAY,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;SACxC,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,GAAG,uBAAuB,CAAC,gBAAgB,CAAC,CAAC;QACzD,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QACjC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,cAAc;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC;QAC5B,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,CAAC;QACpB,OAAO;YACL,GAAG;YACH,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACnD,CAAC;IACJ,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC5F,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;QACrB,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM;YAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACxD,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI;YAAE,OAAO;QAEjE,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,IAAI,UAAsC,CAAC;YAC3C,MAAM,MAAM,GAAG,CAAC,KAAa,EAAQ,EAAE;gBACrC,IAAI,OAAO;oBAAE,OAAO;gBACpB,OAAO,GAAG,IAAI,CAAC;gBACf,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,IAAI,UAAU;oBAAE,YAAY,CAAC,UAAU,CAAC,CAAC;gBACzC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAC1B,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC5B,IAAI,KAAK;oBAAE,MAAM,CAAC,KAAK,CAAC,CAAC;;oBACpB,OAAO,EAAE,CAAC;YACjB,CAAC,CAAC;YACF,MAAM,MAAM,GAAG,GAAS,EAAE,CAAC,MAAM,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,CAAC,KAAY,EAAQ,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACtD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,MAAM,CAAC,IAAI,KAAK,CAAC,4CAA4C,KAAK,CAAC,GAAG,IAAI,SAAS,WAAW,CAAC,CAAC,CAAC;YACnG,CAAC,EAAE,mBAAmB,CAAC,kBAAkB,CAAC,CAAC;YAC3C,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7B,IAAI,CAAC;gBACH,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;gBAClB,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE;wBAC3B,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;4BACnC,MAAM,CAAC,IAAI,KAAK,CAAC,qCAAqC,KAAK,CAAC,GAAG,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;wBACpF,CAAC;oBACH,CAAC,EAAE,KAAK,CAAC,CAAC;oBACV,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC;gBACvB,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACpE,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,YAAY,CAAC,QAAgC;QAC3C,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACvC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzD,CAAC;CACF;AAED,SAAS,uBAAuB,CAAC,KAAc;IAC7C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC1D,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ;QAC3E,CAAC,CAAC,MAAM,CAAC,UAAqC;QAC9C,CAAC,CAAC,SAAS,CAAC;IACd,KAAK,MAAM,SAAS,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QAChF,IAAI,OAAO,SAAS,KAAK,QAAQ;YAAE,SAAS;QAC5C,MAAM,KAAK,GAAG,+DAA+D,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC9F,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"CodexProcessManager.js","sourceRoot":"","sources":["../../src/codex/CodexProcessManager.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ import { mapCodexNotification } from "./CodexEventMapper.js";
5
5
  import { CodexLocalActivityDetector } from "./CodexLocalActivityDetector.js";
6
6
  import { detectProjectlessWorkspace } from "./ProjectlessWorkspace.js";
7
7
  import { threadWriterLockPath } from "./ThreadWriterProcess.js";
8
+ import { assertProviderSettingsApplied, PROVIDER_SWITCH_BUSY, providerSwitchFailure } from "./ProviderSwitch.js";
8
9
  const WINDOWS_SCREENSHOT_DEVELOPER_INSTRUCTIONS = [
9
10
  "When capturing any screenshot on Windows, use one fresh process and make it Per-Monitor DPI Aware V2 before loading System.Windows.Forms, System.Drawing, or UI Automation, and before calling any screen, window, or bounds API.",
10
11
  "Call user32!SetProcessDpiAwarenessContext((IntPtr)-4) in that same process and verify that it succeeded or that the process is already PMv2 before continuing.",
@@ -16,10 +17,10 @@ const WINDOWS_SCREENSHOT_DEVELOPER_INSTRUCTIONS = [
16
17
  const SESSION_REQUEST_TIMEOUT_MS = 60_000;
17
18
  const CONTROL_REQUEST_TIMEOUT_MS = 10_000;
18
19
  const SYNC_REQUEST_TIMEOUT_MS = 5_000;
19
- const THREAD_TURN_PAGE_SIZE = 100;
20
20
  const FORK_SOURCE_TURN_PAGE_SIZE = 20;
21
21
  const FORK_SOURCE_REQUEST_TIMEOUT_MS = 15_000;
22
22
  const USER_RESUMABLE_THREAD_SOURCE_KINDS = ["cli", "vscode", "exec", "appServer"];
23
+ const BUILT_IN_CODEX_PROVIDER_ID = "openai";
23
24
  // A timed-out fork keeps running in App Server and can create an orphan thread.
24
25
  // Wait for its response; connection closure still rejects the request.
25
26
  const FORK_REQUEST_TIMEOUT_MS = 0;
@@ -34,9 +35,9 @@ export class CodexRuntime {
34
35
  unsubscribe;
35
36
  unsubscribeDisconnect;
36
37
  sessionSyncs = new Map();
38
+ sessionOperations = new Map();
37
39
  localActivityDetector;
38
40
  codexHome;
39
- threadListSortKey = "recency_at";
40
41
  releaseInFlight;
41
42
  constructor(provider, logger) {
42
43
  this.provider = provider;
@@ -56,6 +57,9 @@ export class CodexRuntime {
56
57
  return this.codexHome ? threadWriterLockPath(this.codexHome, remoteSessionId) : undefined;
57
58
  }
58
59
  async createSession(input) {
60
+ return this.runSessionOperation(input.localSessionId, () => this.createSessionNow(input));
61
+ }
62
+ async createSessionNow(input) {
59
63
  const client = await this.client();
60
64
  const response = await client.request("thread/start", {
61
65
  cwd: input.cwd,
@@ -80,6 +84,9 @@ export class CodexRuntime {
80
84
  return session;
81
85
  }
82
86
  async resumeSession(input) {
87
+ return this.runSessionOperation(input.localSessionId, () => this.resumeSessionNow(input));
88
+ }
89
+ async resumeSessionNow(input) {
83
90
  const client = await this.client();
84
91
  const response = await client.request("thread/resume", {
85
92
  threadId: input.remoteSessionId,
@@ -96,6 +103,9 @@ export class CodexRuntime {
96
103
  return session;
97
104
  }
98
105
  async forkSession(input) {
106
+ return this.runSessionOperation(input.localSessionId, () => this.forkSessionNow(input));
107
+ }
108
+ async forkSessionNow(input) {
99
109
  const client = await this.client();
100
110
  const forkParams = {
101
111
  threadId: input.remoteSessionId,
@@ -107,19 +117,10 @@ export class CodexRuntime {
107
117
  ...threadLifecycleParams(input.cwd),
108
118
  ...permissionParams(input.permissionMode),
109
119
  };
110
- let response;
111
- try {
112
- response = await client.request("thread/fork", {
113
- ...forkParams,
114
- excludeTurns: true,
115
- }, FORK_REQUEST_TIMEOUT_MS);
116
- }
117
- catch (error) {
118
- if (!isUnsupportedExcludeTurnsError(error))
119
- throw error;
120
- this.logger.warn({ error }, "App Server does not support thread/fork excludeTurns; retrying without it.");
121
- response = await client.request("thread/fork", forkParams, FORK_REQUEST_TIMEOUT_MS);
122
- }
120
+ const response = await client.request("thread/fork", {
121
+ ...forkParams,
122
+ excludeTurns: true,
123
+ }, FORK_REQUEST_TIMEOUT_MS);
123
124
  const requestedTitle = normalizeTaskTitle(input.title);
124
125
  if (requestedTitle) {
125
126
  await client.request("thread/name/set", {
@@ -135,9 +136,13 @@ export class CodexRuntime {
135
136
  return session;
136
137
  }
137
138
  async startTurn(sessionId, prompt) {
139
+ return this.runSessionOperation(sessionId, () => this.startTurnNow(sessionId, prompt));
140
+ }
141
+ async startTurnNow(sessionId, prompt) {
138
142
  const session = this.requireSession(sessionId);
139
143
  const client = await this.client();
140
144
  await this.ensureSessionResumed(session, client);
145
+ session.canReplaceEmptyThread = false;
141
146
  const start = () => client.request("turn/start", {
142
147
  threadId: session.remoteSessionId,
143
148
  input: codexUserInput(prompt),
@@ -172,24 +177,14 @@ export class CodexRuntime {
172
177
  }
173
178
  async listRemoteSessions(input = {}) {
174
179
  const client = await this.client();
175
- let response;
176
- try {
177
- response = await this.requestThreadList(client, input, this.threadListSortKey);
178
- }
179
- catch (error) {
180
- if (this.threadListSortKey !== "recency_at" || !isUnsupportedRecencySortError(error))
181
- throw error;
182
- this.threadListSortKey = "updated_at";
183
- this.logger.warn({ error }, "App Server does not support thread/list recency_at sorting; retrying with updated_at.");
184
- response = await this.requestThreadList(client, input, this.threadListSortKey);
185
- }
180
+ const response = await this.requestThreadList(client, input);
186
181
  const sessions = await Promise.all(response.data.map(async (thread) => {
187
182
  const listed = remoteSessionSummary(thread);
188
183
  if (listed.status === "active" || listed.lastTurnStatus === "inProgress")
189
184
  return listed;
190
185
  try {
191
- const detail = await client.request("thread/read", { threadId: thread.id, includeTurns: true }, SYNC_REQUEST_TIMEOUT_MS);
192
- return mergeRemoteSessionSummary(listed, remoteSessionSummary(detail.thread));
186
+ const turns = await this.readLatestThreadTurns(client, thread.id, "summary");
187
+ return mergeRemoteSessionSummary(listed, remoteSessionSummary({ ...thread, turns }));
193
188
  }
194
189
  catch {
195
190
  return listed;
@@ -203,35 +198,39 @@ export class CodexRuntime {
203
198
  nextCursor: response.nextCursor ?? undefined,
204
199
  };
205
200
  }
206
- requestThreadList(client, input, sortKey) {
201
+ requestThreadList(client, input) {
207
202
  return client.request("thread/list", {
208
203
  cursor: input.cursor,
209
204
  limit: input.limit ?? 20,
210
- sortKey,
205
+ sortKey: this.provider.getAgentFamily?.() === "traex" ? "updated_at" : "recency_at",
211
206
  sortDirection: "desc",
212
207
  sourceKinds: USER_RESUMABLE_THREAD_SOURCE_KINDS,
213
208
  archived: false,
214
209
  searchTerm: input.searchTerm,
215
210
  }, CONTROL_REQUEST_TIMEOUT_MS);
216
211
  }
217
- async readRemoteSession(remoteSessionId) {
212
+ async readRemoteSession(remoteSessionId, view = "latest") {
218
213
  const client = await this.client();
219
- const response = await this.readThreadWithTurns(client, remoteSessionId);
220
- return this.decorateRemoteSession(remoteSessionId, remoteSessionSummary(response.thread));
214
+ const response = await client.request("thread/read", { threadId: remoteSessionId, includeTurns: false }, SYNC_REQUEST_TIMEOUT_MS);
215
+ const turns = view === "metadata" ? []
216
+ : await this.readLatestThreadTurns(client, remoteSessionId, view === "latest-full" ? "full" : "summary");
217
+ return this.decorateRemoteSession(remoteSessionId, remoteSessionSummary({ ...response.thread, turns }));
218
+ }
219
+ async listRemoteTurnSummaries(remoteSessionId, input) {
220
+ const response = await this.readTurnPage(await this.client(), remoteSessionId, {
221
+ cursor: input.cursor,
222
+ limit: Math.max(1, Math.min(100, Math.trunc(input.limit))),
223
+ itemsView: "summary",
224
+ });
225
+ return {
226
+ turns: remoteSessionSummary({ id: remoteSessionId, turns: response.data }).completedTurns ?? [],
227
+ nextCursor: response.nextCursor ?? undefined,
228
+ };
221
229
  }
222
230
  async readRemoteForkSource(remoteSessionId) {
223
231
  const client = await this.client();
224
232
  const metadata = await client.request("thread/read", { threadId: remoteSessionId, includeTurns: false }, FORK_SOURCE_REQUEST_TIMEOUT_MS);
225
- let turns;
226
- try {
227
- turns = await this.listRecentThreadTurnsThroughCompleted(client, remoteSessionId);
228
- }
229
- catch (error) {
230
- if (!isUnsupportedThreadTurnsListError(error))
231
- throw error;
232
- this.logger.warn({ error, remoteSessionId }, "App Server does not support lightweight Fork source reads; falling back to complete thread history.");
233
- return this.readRemoteSession(remoteSessionId);
234
- }
233
+ const turns = await this.listRecentThreadTurnsThroughCompleted(client, remoteSessionId);
235
234
  return this.decorateRemoteSession(remoteSessionId, remoteSessionSummary({
236
235
  ...metadata.thread,
237
236
  turns,
@@ -251,12 +250,8 @@ export class CodexRuntime {
251
250
  const seenCursors = new Set();
252
251
  let cursor;
253
252
  do {
254
- const response = await client.request("thread/turns/list", {
255
- threadId: remoteSessionId,
256
- ...(cursor ? { cursor } : {}),
257
- limit: FORK_SOURCE_TURN_PAGE_SIZE,
258
- sortDirection: "desc",
259
- itemsView: "summary",
253
+ const response = await this.readTurnPage(client, remoteSessionId, {
254
+ cursor, limit: FORK_SOURCE_TURN_PAGE_SIZE, itemsView: "summary",
260
255
  }, FORK_SOURCE_REQUEST_TIMEOUT_MS);
261
256
  turns.push(...response.data);
262
257
  const completedIndex = turns.findIndex((turn) => turn.status === "completed");
@@ -276,49 +271,25 @@ export class CodexRuntime {
276
271
  } while (cursor);
277
272
  return turns.reverse();
278
273
  }
279
- async readThreadWithTurns(client, remoteSessionId) {
274
+ async readLatestThreadTurns(client, remoteSessionId, itemsView) {
275
+ const response = await this.readTurnPage(client, remoteSessionId, { limit: 1, itemsView });
276
+ return response.data;
277
+ }
278
+ async readTurnPage(client, remoteSessionId, input, timeoutMs = SYNC_REQUEST_TIMEOUT_MS) {
280
279
  try {
281
- return await client.request("thread/read", { threadId: remoteSessionId, includeTurns: true }, SYNC_REQUEST_TIMEOUT_MS);
280
+ return await client.request("thread/turns/list", {
281
+ threadId: remoteSessionId,
282
+ ...(input.cursor ? { cursor: input.cursor } : {}),
283
+ limit: input.limit, sortDirection: "desc", itemsView: input.itemsView,
284
+ }, timeoutMs);
282
285
  }
283
286
  catch (error) {
284
- const paginated = isPaginatedThreadReadError(error);
285
- if (!paginated && !isUnmaterializedThreadReadError(error))
286
- throw error;
287
- const metadata = await client.request("thread/read", { threadId: remoteSessionId, includeTurns: false }, SYNC_REQUEST_TIMEOUT_MS);
288
- if (!paginated)
289
- return { thread: { ...metadata.thread, turns: [] } };
290
- return {
291
- thread: {
292
- ...metadata.thread,
293
- turns: await this.listThreadTurns(client, remoteSessionId),
294
- },
295
- };
296
- }
297
- }
298
- async listThreadTurns(client, remoteSessionId) {
299
- const turns = [];
300
- const seenCursors = new Set();
301
- let cursor;
302
- do {
303
- const response = await client.request("thread/turns/list", {
304
- threadId: remoteSessionId,
305
- ...(cursor ? { cursor } : {}),
306
- limit: THREAD_TURN_PAGE_SIZE,
307
- sortDirection: "asc",
308
- itemsView: "full",
309
- }, SYNC_REQUEST_TIMEOUT_MS);
310
- turns.push(...response.data);
311
- const nextCursor = response.nextCursor ?? undefined;
312
- if (!nextCursor)
313
- break;
314
- if (seenCursors.has(nextCursor)) {
315
- this.logger.warn({ remoteSessionId, cursor: nextCursor }, "App Server repeated a thread Turn pagination cursor; stopping pagination.");
316
- break;
287
+ if (error instanceof AppServerRequestError && error.method === "thread/turns/list"
288
+ && /not materialized yet; thread\/turns\/list is unavailable before first user message/i.test(error.serverMessage)) {
289
+ return { data: [], nextCursor: null };
317
290
  }
318
- seenCursors.add(nextCursor);
319
- cursor = nextCursor;
320
- } while (cursor);
321
- return turns;
291
+ throw error;
292
+ }
322
293
  }
323
294
  async inspectRemoteSessionActivity(remoteSessionId) {
324
295
  const [response, activeThreads] = await Promise.all([
@@ -341,7 +312,7 @@ export class CodexRuntime {
341
312
  const existing = this.sessionSyncs.get(sessionId);
342
313
  if (existing)
343
314
  return existing;
344
- const synchronization = this.synchronizeSessionNow(sessionId);
315
+ const synchronization = this.runSessionOperation(sessionId, () => this.synchronizeSessionNow(sessionId));
345
316
  this.sessionSyncs.set(sessionId, synchronization);
346
317
  try {
347
318
  return await synchronization;
@@ -368,8 +339,10 @@ export class CodexRuntime {
368
339
  this.logger.info({ ...(sessionId ? { sessionId } : {}), threadId: remoteSessionId, turnId }, "App Server accepted the turn interrupt request.");
369
340
  }
370
341
  async closeSession(sessionId) {
371
- const session = this.requireSession(sessionId);
372
- await this.archiveRemoteSession(session.remoteSessionId);
342
+ await this.runSessionOperation(sessionId, async () => {
343
+ const session = this.requireSession(sessionId);
344
+ await this.archiveRemoteSession(session.remoteSessionId);
345
+ });
373
346
  }
374
347
  async archiveRemoteSession(remoteSessionId) {
375
348
  await (await this.client()).request("thread/archive", { threadId: remoteSessionId }, CONTROL_REQUEST_TIMEOUT_MS);
@@ -379,6 +352,9 @@ export class CodexRuntime {
379
352
  }
380
353
  }
381
354
  async setTitle(sessionId, title) {
355
+ return this.runSessionOperation(sessionId, () => this.setTitleNow(sessionId, title));
356
+ }
357
+ async setTitleNow(sessionId, title) {
382
358
  const session = this.requireSession(sessionId);
383
359
  const normalizedTitle = normalizeTaskTitle(title);
384
360
  if (!normalizedTitle)
@@ -387,6 +363,9 @@ export class CodexRuntime {
387
363
  session.title = normalizedTitle;
388
364
  }
389
365
  async getGoal(sessionId) {
366
+ return this.runSessionOperation(sessionId, () => this.getGoalNow(sessionId));
367
+ }
368
+ async getGoalNow(sessionId) {
390
369
  const session = this.requireSession(sessionId);
391
370
  const client = await this.client();
392
371
  await this.ensureSessionResumed(session, client);
@@ -394,13 +373,20 @@ export class CodexRuntime {
394
373
  return response.goal ?? undefined;
395
374
  }
396
375
  async setGoal(sessionId, update) {
376
+ return this.runSessionOperation(sessionId, () => this.setGoalNow(sessionId, update));
377
+ }
378
+ async setGoalNow(sessionId, update) {
397
379
  const session = this.requireSession(sessionId);
398
380
  const client = await this.client();
399
381
  await this.ensureSessionResumed(session, client);
382
+ session.canReplaceEmptyThread = false;
400
383
  const response = await client.request("thread/goal/set", { threadId: session.remoteSessionId, ...update }, CONTROL_REQUEST_TIMEOUT_MS);
401
384
  return response.goal;
402
385
  }
403
386
  async clearGoal(sessionId) {
387
+ return this.runSessionOperation(sessionId, () => this.clearGoalNow(sessionId));
388
+ }
389
+ async clearGoalNow(sessionId) {
404
390
  const session = this.requireSession(sessionId);
405
391
  const client = await this.client();
406
392
  await this.ensureSessionResumed(session, client);
@@ -408,32 +394,132 @@ export class CodexRuntime {
408
394
  return response.cleared;
409
395
  }
410
396
  async setModel(sessionId, model) {
411
- this.requireSession(sessionId).model = model;
397
+ await this.runSessionOperation(sessionId, async () => { this.requireSession(sessionId).model = model; });
412
398
  }
413
399
  async setReasoningEffort(sessionId, effort) {
414
- this.requireSession(sessionId).reasoningEffort = effort;
400
+ await this.runSessionOperation(sessionId, async () => { this.requireSession(sessionId).reasoningEffort = effort; });
415
401
  }
416
402
  async setPermissionMode(sessionId, mode) {
417
- this.requireSession(sessionId).permissionMode = mode;
403
+ await this.runSessionOperation(sessionId, async () => { this.requireSession(sessionId).permissionMode = mode; });
404
+ }
405
+ async setExecutionSettings(sessionId, settings, persist) {
406
+ return this.runSessionOperation(sessionId, () => this.setExecutionSettingsNow(sessionId, settings, persist));
418
407
  }
419
- async setExecutionSettings(sessionId, settings) {
408
+ async setExecutionSettingsNow(sessionId, settings, persist) {
420
409
  const session = this.requireSession(sessionId);
421
- const response = await (await this.client()).request("thread/resume", {
422
- threadId: session.remoteSessionId,
423
- excludeTurns: true,
424
- cwd: session.cwd,
425
- modelProvider: settings.modelProvider,
426
- model: settings.model,
427
- ...threadLifecycleParams(session.cwd),
428
- ...permissionParams(settings.permissionMode),
429
- }, SESSION_REQUEST_TIMEOUT_MS);
430
- session.modelProvider = response.modelProvider ?? settings.modelProvider;
431
- session.model = response.model ?? settings.model;
432
- session.reasoningEffort = settings.reasoningEffort;
433
- session.permissionMode = settings.permissionMode;
434
- session.needsResume = false;
410
+ if (session.activeTurnId)
411
+ throw new Error(PROVIDER_SWITCH_BUSY);
412
+ const client = await this.client();
413
+ const metadata = await client.request("thread/read", {
414
+ threadId: session.remoteSessionId, includeTurns: false,
415
+ }, SYNC_REQUEST_TIMEOUT_MS);
416
+ const localActive = await this.localActivityDetector?.activeThreads([session.remoteSessionId]);
417
+ if (metadata.thread.status?.type === "active" || localActive?.has(session.remoteSessionId) || session.activeTurnId) {
418
+ throw new Error(PROVIDER_SWITCH_BUSY);
419
+ }
420
+ let empty = false;
421
+ if (session.canReplaceEmptyThread && !metadata.thread.forkedFromId && !metadata.thread.preview?.trim()) {
422
+ try {
423
+ const turns = await this.readLatestThreadTurns(client, session.remoteSessionId, "summary");
424
+ empty = turns.length === 0;
425
+ if (!empty)
426
+ session.canReplaceEmptyThread = false;
427
+ }
428
+ catch (error) {
429
+ if (!(error instanceof AppServerRequestError) || error.method !== "thread/turns/list"
430
+ || !/missing source rollout|no rollout found/iu.test(error.serverMessage))
431
+ throw error;
432
+ empty = true;
433
+ }
434
+ }
435
+ const previous = { ...session };
436
+ let replacementId;
437
+ let detached = false;
438
+ try {
439
+ const params = {
440
+ cwd: session.cwd, modelProvider: settings.modelProvider, model: settings.model,
441
+ ...threadLifecycleParams(session.cwd), ...permissionParams(settings.permissionMode),
442
+ };
443
+ if (!empty) {
444
+ await this.detachThreadForSettings(client, session);
445
+ detached = true;
446
+ }
447
+ const response = empty
448
+ ? await client.request("thread/start", {
449
+ ...params, threadSource: "user", allowProviderModelFallback: false,
450
+ }, SESSION_REQUEST_TIMEOUT_MS)
451
+ : await client.request("thread/resume", {
452
+ ...params, threadId: session.remoteSessionId, excludeTurns: true,
453
+ }, SESSION_REQUEST_TIMEOUT_MS);
454
+ if (empty)
455
+ replacementId = response.thread.id;
456
+ assertProviderSettingsApplied(response, settings);
457
+ if (!empty && response.thread.id !== session.remoteSessionId)
458
+ throw new Error("App Server 返回了不同的任务 ID,未保存设置。");
459
+ if (empty && session.title)
460
+ await client.request("thread/name/set", {
461
+ threadId: response.thread.id, name: session.title,
462
+ }, SESSION_REQUEST_TIMEOUT_MS);
463
+ const candidate = { ...session, ...settings, remoteSessionId: response.thread.id,
464
+ needsResume: false, settingsRecoveryError: undefined };
465
+ await persist?.(candidate);
466
+ Object.assign(session, candidate);
467
+ }
468
+ catch (error) {
469
+ if (replacementId)
470
+ await this.discardUnusedThread(client, replacementId);
471
+ let recovery = "原任务设置未更改。";
472
+ if (detached) {
473
+ try {
474
+ await this.detachThreadForSettings(client, previous);
475
+ const restored = await client.request("thread/resume", {
476
+ threadId: previous.remoteSessionId, excludeTurns: true, cwd: previous.cwd,
477
+ modelProvider: previous.modelProvider, model: previous.model,
478
+ ...threadLifecycleParams(previous.cwd), ...permissionParams(previous.permissionMode),
479
+ }, SESSION_REQUEST_TIMEOUT_MS);
480
+ if (previous.modelProvider && previous.model)
481
+ assertProviderSettingsApplied(restored, {
482
+ modelProvider: previous.modelProvider, model: previous.model,
483
+ });
484
+ if (restored.thread.id !== previous.remoteSessionId)
485
+ throw new Error("恢复时返回了不同的任务 ID。");
486
+ session.needsResume = false;
487
+ session.settingsRecoveryError = undefined;
488
+ }
489
+ catch (restoreError) {
490
+ this.logger.warn({ error: restoreError, sessionId }, "Failed to restore Provider after a rejected settings change.");
491
+ recovery = "原设置仍保留,但远端恢复失败;请重新切换 Provider,确认成功后再发送消息。";
492
+ session.settingsRecoveryError = recovery;
493
+ session.needsResume = true;
494
+ }
495
+ }
496
+ throw new Error(`Provider 切换失败:${providerSwitchFailure(error)} ${recovery}`, { cause: error });
497
+ }
498
+ if (replacementId)
499
+ await this.discardUnusedThread(client, previous.remoteSessionId);
435
500
  return session;
436
501
  }
502
+ async detachThreadForSettings(client, session) {
503
+ const current = await client.request("thread/read", {
504
+ threadId: session.remoteSessionId, includeTurns: false,
505
+ }, SYNC_REQUEST_TIMEOUT_MS);
506
+ if (session.activeTurnId || current.thread.status?.type === "active")
507
+ throw new Error(PROVIDER_SWITCH_BUSY);
508
+ const result = await client.request("thread/unsubscribe", {
509
+ threadId: session.remoteSessionId,
510
+ }, CONTROL_REQUEST_TIMEOUT_MS);
511
+ if (result.status !== "unsubscribed" && result.status !== "notLoaded") {
512
+ throw new Error("当前任务仍由其他客户端加载,无法安全切换 Provider。请先在原客户端释放任务。");
513
+ }
514
+ }
515
+ async discardUnusedThread(client, threadId) {
516
+ try {
517
+ await client.request("thread/unsubscribe", { threadId }, CONTROL_REQUEST_TIMEOUT_MS);
518
+ }
519
+ catch (error) {
520
+ this.logger.warn({ error, threadId }, "Failed to release an unused empty Thread after changing Provider.");
521
+ }
522
+ }
437
523
  async respondToApproval(sessionId, requestId, decision) {
438
524
  const pending = this.approvals.get(requestId);
439
525
  if (!pending || pending.sessionId !== sessionId)
@@ -471,6 +557,13 @@ export class CodexRuntime {
471
557
  ...(id === defaultProvider ? { isDefault: true } : {}),
472
558
  });
473
559
  }
560
+ if (this.provider.getAgentFamily?.() === "codex" && !providers.has(BUILT_IN_CODEX_PROVIDER_ID)) {
561
+ providers.set(BUILT_IN_CODEX_PROVIDER_ID, {
562
+ id: BUILT_IN_CODEX_PROVIDER_ID,
563
+ displayName: "OpenAI",
564
+ ...(!defaultProvider ? { isDefault: true } : {}),
565
+ });
566
+ }
474
567
  if (defaultProvider && !providers.has(defaultProvider)) {
475
568
  providers.set(defaultProvider, { id: defaultProvider, isDefault: true });
476
569
  }
@@ -484,7 +577,7 @@ export class CodexRuntime {
484
577
  if (this.releaseInFlight)
485
578
  return this.releaseInFlight;
486
579
  const activeSessionIds = [...this.sessions.values()]
487
- .filter((session) => Boolean(session.activeTurnId))
580
+ .filter((session) => Boolean(session.activeTurnId) || this.sessionOperations.has(session.localSessionId))
488
581
  .map((session) => session.localSessionId);
489
582
  if (activeSessionIds.length > 0 && options.force !== true) {
490
583
  return { status: "busy", activeSessionIds };
@@ -731,6 +824,8 @@ export class CodexRuntime {
731
824
  ?? models.find((item) => item.isDefault)?.defaultReasoningEffort;
732
825
  }
733
826
  async ensureSessionResumed(session, client) {
827
+ if (session.settingsRecoveryError)
828
+ throw new Error(session.settingsRecoveryError);
734
829
  if (!session.needsResume)
735
830
  return;
736
831
  await this.resumeAppServerSession(session, client);
@@ -749,9 +844,12 @@ export class CodexRuntime {
749
844
  }
750
845
  async synchronizeSessionNow(sessionId) {
751
846
  const session = this.requireSession(sessionId);
752
- const response = await (await this.client()).request("thread/read", { threadId: session.remoteSessionId, includeTurns: true }, SYNC_REQUEST_TIMEOUT_MS);
753
- this.reconcileThreadSnapshot(session, response.thread);
754
- return session;
847
+ const client = await this.client();
848
+ const response = await client.request("thread/read", { threadId: session.remoteSessionId, includeTurns: false }, SYNC_REQUEST_TIMEOUT_MS);
849
+ const turns = await this.readLatestThreadTurns(client, session.remoteSessionId, "full");
850
+ const thread = { ...response.thread, turns };
851
+ this.reconcileThreadSnapshot(session, thread);
852
+ return { ...session, remoteSummary: await this.decorateRemoteSession(session.remoteSessionId, remoteSessionSummary(thread)) };
755
853
  }
756
854
  reconcileThreadSnapshot(session, thread) {
757
855
  const turns = Array.isArray(thread.turns) ? thread.turns : [];
@@ -796,6 +894,7 @@ export class CodexRuntime {
796
894
  this.finishSnapshotTurn(session, latest);
797
895
  }
798
896
  adoptTurn(session, turnId, startedAt) {
897
+ session.canReplaceEmptyThread = false;
799
898
  session.activeTurnId = turnId;
800
899
  session.activeTurnStartedAt = startedAt;
801
900
  session.finalText = "";
@@ -873,6 +972,7 @@ export class CodexRuntime {
873
972
  generatedImagePaths: [],
874
973
  messagePhases: new Map(),
875
974
  needsResume: false,
975
+ canReplaceEmptyThread: !("remoteSessionId" in input),
876
976
  };
877
977
  }
878
978
  requireSession(sessionId) {
@@ -881,6 +981,18 @@ export class CodexRuntime {
881
981
  throw new Error(`Unknown App Server session: ${sessionId}`);
882
982
  return session;
883
983
  }
984
+ async runSessionOperation(sessionId, operation) {
985
+ const previous = this.sessionOperations.get(sessionId);
986
+ const current = (previous ?? Promise.resolve()).catch(() => undefined).then(operation);
987
+ this.sessionOperations.set(sessionId, current);
988
+ try {
989
+ return await current;
990
+ }
991
+ finally {
992
+ if (this.sessionOperations.get(sessionId) === current)
993
+ this.sessionOperations.delete(sessionId);
994
+ }
995
+ }
884
996
  requireActiveTurn(sessionId, turnId) {
885
997
  const session = this.requireSession(sessionId);
886
998
  if (session.activeTurnId !== turnId)
@@ -976,62 +1088,11 @@ function extractGeneratedImagePaths(turn) {
976
1088
  function uniqueStrings(values) {
977
1089
  return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
978
1090
  }
979
- function isUnsupportedExcludeTurnsError(error) {
980
- if (!(error instanceof AppServerRequestError) || error.method !== "thread/fork")
981
- return false;
982
- const data = typeof error.data === "string"
983
- ? error.data
984
- : error.data === undefined
985
- ? ""
986
- : JSON.stringify(error.data);
987
- const details = `${error.serverMessage} ${data}`;
988
- if (!details.toLowerCase().replaceAll(/[^a-z]/g, "").includes("excludeturns"))
989
- return false;
990
- return error.code === -32602
991
- || /unknown|unrecognized|unexpected|unsupported|not supported|experimental/i.test(details);
992
- }
993
1091
  function isInvalidWorkingDirectoryError(error) {
994
1092
  return error instanceof AppServerRequestError
995
1093
  && error.method === "turn/start"
996
1094
  && /invalid cwd/i.test(`${error.serverMessage} ${error.data === undefined ? "" : JSON.stringify(error.data)}`);
997
1095
  }
998
- function isUnsupportedRecencySortError(error) {
999
- if (!(error instanceof AppServerRequestError) || error.method !== "thread/list")
1000
- return false;
1001
- const data = typeof error.data === "string"
1002
- ? error.data
1003
- : error.data === undefined
1004
- ? ""
1005
- : JSON.stringify(error.data);
1006
- const details = `${error.serverMessage} ${data}`;
1007
- return /recency_at/i.test(details)
1008
- && /unknown|unrecognized|unexpected|unsupported|not supported|invalid/i.test(details);
1009
- }
1010
- function isPaginatedThreadReadError(error) {
1011
- const details = appServerErrorDetails(error);
1012
- return /paginated threads?[^\n]*do(?:es)? not support[^\n]*thread\/read[^\n]*includeTurns\s*=\s*true/i.test(details);
1013
- }
1014
- function isUnsupportedThreadTurnsListError(error) {
1015
- if (!(error instanceof AppServerRequestError) || error.method !== "thread/turns/list")
1016
- return false;
1017
- return error.code === -32601
1018
- || /method not found|unknown method|unsupported|not supported/i.test(appServerErrorDetails(error));
1019
- }
1020
- function isUnmaterializedThreadReadError(error) {
1021
- const details = appServerErrorDetails(error);
1022
- return /thread[^\n]*(?:not materialized|not loaded)[^\n]*includeTurns[^\n]*(?:unavailable|before first user message)/i
1023
- .test(details);
1024
- }
1025
- function appServerErrorDetails(error) {
1026
- if (!(error instanceof AppServerRequestError))
1027
- return error instanceof Error ? error.message : String(error);
1028
- const data = typeof error.data === "string"
1029
- ? error.data
1030
- : error.data === undefined
1031
- ? ""
1032
- : JSON.stringify(error.data);
1033
- return `${error.message} ${error.serverMessage} ${data}`;
1034
- }
1035
1096
  function remoteSessionSummary(thread) {
1036
1097
  const lastTurn = thread.turns?.at(-1);
1037
1098
  const lastCompletedTurn = [...(thread.turns ?? [])]