lark-remote 0.1.6 → 0.1.8
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 +51 -1
- package/README.en.md +9 -1
- package/README.md +14 -2
- package/dist/bridge/approval-coordinator.d.ts +71 -34
- package/dist/bridge/approval-coordinator.js +225 -41
- package/dist/bridge/inbound-media.d.ts +62 -0
- package/dist/bridge/inbound-media.js +282 -0
- package/dist/bridge/index.d.ts +97 -13
- package/dist/bridge/index.js +227 -87
- package/dist/bridge/queue-manager.d.ts +2 -1
- package/dist/bridge/queue-manager.js +1 -0
- package/dist/card/approval-render.js +294 -36
- package/dist/card/bash-card-session.d.ts +2 -24
- package/dist/card/bash-card-session.js +6 -73
- package/dist/card/bash-renderer.js +2 -3
- package/dist/card/card-budget.js +29 -75
- package/dist/card/card-session.d.ts +48 -0
- package/dist/card/card-session.js +86 -0
- package/dist/card/card-shared.d.ts +7 -16
- package/dist/card/card-shared.js +7 -30
- package/dist/card/run-card-session.d.ts +2 -55
- package/dist/card/run-card-session.js +6 -104
- package/dist/card/run-renderer.d.ts +7 -0
- package/dist/card/run-renderer.js +116 -238
- package/dist/card/run-state.d.ts +13 -0
- package/dist/card/run-state.js +25 -16
- package/dist/card/text-truncate.d.ts +13 -12
- package/dist/card/text-truncate.js +13 -63
- package/dist/common/fs.d.ts +8 -0
- package/dist/common/fs.js +17 -0
- package/dist/common/guards.d.ts +0 -20
- package/dist/common/guards.js +0 -43
- package/dist/common/sleep.d.ts +2 -0
- package/dist/common/sleep.js +5 -0
- package/dist/common/truncate.d.ts +39 -0
- package/dist/common/truncate.js +92 -0
- package/dist/config/codex-config.d.ts +1 -1
- package/dist/config/codex-config.js +1 -1
- package/dist/config/index.d.ts +112 -24
- package/dist/config/index.js +139 -37
- package/dist/connector/index.d.ts +69 -2
- package/dist/connector/index.js +107 -1
- package/dist/index.js +150 -118
- package/dist/instance-lock.js +4 -12
- package/dist/order/alias-resolve.d.ts +13 -0
- package/dist/order/alias-resolve.js +22 -0
- package/dist/order/alias-store.d.ts +38 -0
- package/dist/order/alias-store.js +106 -0
- package/dist/persistence/atomic-write.d.ts +8 -2
- package/dist/persistence/atomic-write.js +23 -7
- package/dist/restart.js +2 -1
- package/dist/router/card-action-payload.d.ts +30 -0
- package/dist/router/card-action-payload.js +39 -0
- package/dist/router/card-helpers.d.ts +63 -0
- package/dist/router/card-helpers.js +146 -0
- package/dist/router/config/claude.js +30 -1
- package/dist/router/config/codex.js +29 -50
- package/dist/router/config/common/model-patch.d.ts +22 -0
- package/dist/router/config/common/model-patch.js +26 -0
- package/dist/router/config/common/render.js +5 -4
- package/dist/router/config/dsh.d.ts +51 -0
- package/dist/router/config/dsh.js +179 -0
- package/dist/router/config/index.d.ts +12 -0
- package/dist/router/config/index.js +28 -1
- package/dist/router/config/kimi.d.ts +4 -1
- package/dist/router/config/kimi.js +27 -0
- package/dist/router/config/opencode.js +18 -5
- package/dist/router/config/pi.js +4 -5
- package/dist/router/config/types.d.ts +17 -2
- package/dist/router/index.d.ts +76 -22
- package/dist/router/index.js +479 -548
- package/dist/router/utils.d.ts +2 -0
- package/dist/router/utils.js +16 -2
- package/dist/runner/agent-choices-common.d.ts +16 -0
- package/dist/runner/agent-choices-common.js +34 -0
- package/dist/runner/claude/runner.d.ts +47 -10
- package/dist/runner/claude/runner.js +117 -46
- package/dist/runner/claude/session.d.ts +166 -0
- package/dist/runner/claude/session.js +768 -0
- package/dist/runner/codex/app-server/protocol-types.d.ts +66 -39
- package/dist/runner/codex/app-server/protocol-types.js +2 -13
- package/dist/runner/codex/app-server/runner.d.ts +52 -59
- package/dist/runner/codex/app-server/runner.js +216 -338
- package/dist/runner/codex/app-server/translator.d.ts +8 -21
- package/dist/runner/codex/app-server/translator.js +45 -18
- package/dist/runner/codex/index.d.ts +0 -2
- package/dist/runner/codex/index.js +0 -2
- package/dist/runner/common/acp/protocol-helpers.d.ts +14 -0
- package/dist/runner/common/acp/protocol-helpers.js +21 -0
- package/dist/runner/common/acp/protocol-types.d.ts +271 -0
- package/dist/runner/common/acp/protocol-types.js +77 -0
- package/dist/runner/common/connection-based-runner.d.ts +111 -0
- package/dist/runner/common/connection-based-runner.js +261 -0
- package/dist/runner/{codex/app-server → common/jsonrpc}/client.d.ts +14 -7
- package/dist/runner/{codex/app-server → common/jsonrpc}/client.js +27 -18
- package/dist/runner/common/jsonrpc/connection-manager.d.ts +125 -0
- package/dist/runner/{codex/app-server → common/jsonrpc}/connection-manager.js +63 -28
- package/dist/runner/{codex/app-server → common/jsonrpc}/transport.d.ts +5 -4
- package/dist/runner/{codex/app-server → common/jsonrpc}/transport.js +18 -19
- package/dist/runner/common/jsonrpc/types.d.ts +32 -0
- package/dist/runner/common/jsonrpc/types.js +9 -0
- package/dist/runner/common/process-stopper.d.ts +2 -1
- package/dist/runner/common/process-stopper.js +1 -0
- package/dist/runner/common/runner-utils.d.ts +6 -13
- package/dist/runner/common/runner-utils.js +6 -47
- package/dist/runner/common/spawning-runner.d.ts +24 -70
- package/dist/runner/common/spawning-runner.js +98 -152
- package/dist/runner/dsh/client.d.ts +95 -0
- package/dist/runner/dsh/client.js +418 -0
- package/dist/runner/dsh/fake-dsh-server.d.ts +60 -0
- package/dist/runner/dsh/fake-dsh-server.js +156 -0
- package/dist/runner/dsh/index.d.ts +3 -0
- package/dist/runner/dsh/index.js +4 -0
- package/dist/runner/dsh/runner.d.ts +57 -0
- package/dist/runner/dsh/runner.js +191 -0
- package/dist/runner/dsh/translator.d.ts +28 -0
- package/dist/runner/dsh/translator.js +98 -0
- package/dist/runner/dsh/types.d.ts +155 -0
- package/dist/runner/dsh/types.js +14 -0
- package/dist/runner/index.d.ts +5 -4
- package/dist/runner/index.js +5 -4
- package/dist/runner/kimi/acp/runner.d.ts +106 -0
- package/dist/runner/kimi/acp/runner.js +516 -0
- package/dist/runner/kimi/acp/translator.d.ts +138 -0
- package/dist/runner/kimi/acp/translator.js +441 -0
- package/dist/runner/kimi/index.d.ts +1 -1
- package/dist/runner/kimi/index.js +1 -1
- package/dist/runner/opencode/acp/index.d.ts +1 -0
- package/dist/runner/opencode/acp/index.js +2 -0
- package/dist/runner/opencode/acp/runner.d.ts +121 -0
- package/dist/runner/opencode/acp/runner.js +429 -0
- package/dist/runner/opencode/acp/translator.d.ts +119 -0
- package/dist/runner/opencode/acp/translator.js +345 -0
- package/dist/runner/opencode/index.d.ts +1 -1
- package/dist/runner/opencode/index.js +1 -1
- package/dist/runner/pi/index.d.ts +1 -1
- package/dist/runner/pi/index.js +1 -1
- package/dist/runner/pi/rpc/client.d.ts +52 -0
- package/dist/runner/pi/rpc/client.js +114 -0
- package/dist/runner/pi/rpc/index.d.ts +4 -0
- package/dist/runner/pi/rpc/index.js +4 -0
- package/dist/runner/pi/rpc/protocol-types.d.ts +127 -0
- package/dist/runner/pi/rpc/protocol-types.js +21 -0
- package/dist/runner/pi/rpc/runner.d.ts +74 -0
- package/dist/runner/pi/rpc/runner.js +190 -0
- package/dist/runner/pi/rpc/translator.d.ts +55 -0
- package/dist/runner/pi/rpc/translator.js +258 -0
- package/dist/runner/probe.js +6 -1
- package/dist/runner/question-common.d.ts +26 -0
- package/dist/runner/question-common.js +42 -0
- package/dist/runner/registry.d.ts +7 -8
- package/dist/runner/registry.js +3 -4
- package/dist/runner/resolve-agent-choices.js +18 -59
- package/dist/runner/sync-agent-choices.js +16 -67
- package/dist/runner/types.d.ts +97 -14
- package/dist/session/claude/session-index.js +2 -3
- package/dist/session/claude/session-reader.js +3 -6
- package/dist/session/claude/sessions.js +43 -28
- package/dist/session/codex/rollout-reader.d.ts +2 -2
- package/dist/session/codex/rollout-reader.js +14 -17
- package/dist/session/codex/sessions.d.ts +1 -1
- package/dist/session/codex/sessions.js +2 -2
- package/dist/session/common/content-blocks.d.ts +7 -0
- package/dist/session/common/content-blocks.js +13 -9
- package/dist/session/common/jsonl.d.ts +8 -25
- package/dist/session/common/jsonl.js +126 -261
- package/dist/session/common/pagination.d.ts +28 -0
- package/dist/session/common/pagination.js +33 -0
- package/dist/session/dsh/index.d.ts +1 -0
- package/dist/session/dsh/index.js +2 -0
- package/dist/session/dsh/sessions.d.ts +39 -0
- package/dist/session/dsh/sessions.js +237 -0
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +1 -0
- package/dist/session/kimi/sessions.d.ts +34 -1
- package/dist/session/kimi/sessions.js +208 -98
- package/dist/session/opencode/sessions.d.ts +17 -3
- package/dist/session/opencode/sessions.js +29 -29
- package/dist/session/pi/sessions.d.ts +5 -1
- package/dist/session/pi/sessions.js +12 -16
- package/dist/session/session-store.d.ts +11 -1
- package/dist/session/session-store.js +75 -178
- package/dist/update/index.d.ts +2 -2
- package/dist/update/index.js +2 -2
- package/dist/update/install.d.ts +20 -2
- package/dist/update/install.js +51 -7
- package/dist/workspace/index.d.ts +4 -0
- package/dist/workspace/index.js +8 -1
- package/package.json +2 -1
- package/dist/runner/codex/app-server/connection-manager.d.ts +0 -56
- package/dist/runner/codex/argv.d.ts +0 -33
- package/dist/runner/codex/argv.js +0 -40
- package/dist/runner/codex/factory.d.ts +0 -29
- package/dist/runner/codex/factory.js +0 -39
- package/dist/runner/codex/jsonl.d.ts +0 -37
- package/dist/runner/codex/jsonl.js +0 -221
- package/dist/runner/codex/runner.d.ts +0 -66
- package/dist/runner/codex/runner.js +0 -129
- package/dist/runner/common/exec-translator.d.ts +0 -73
- package/dist/runner/common/exec-translator.js +0 -105
- package/dist/runner/kimi/jsonl.d.ts +0 -28
- package/dist/runner/kimi/jsonl.js +0 -143
- package/dist/runner/kimi/runner.d.ts +0 -49
- package/dist/runner/kimi/runner.js +0 -105
- package/dist/runner/opencode/argv.d.ts +0 -32
- package/dist/runner/opencode/argv.js +0 -35
- package/dist/runner/opencode/jsonl.d.ts +0 -68
- package/dist/runner/opencode/jsonl.js +0 -295
- package/dist/runner/opencode/runner.d.ts +0 -61
- package/dist/runner/opencode/runner.js +0 -93
- package/dist/runner/pi/jsonl.d.ts +0 -128
- package/dist/runner/pi/jsonl.js +0 -249
- package/dist/runner/pi/runner.d.ts +0 -36
- package/dist/runner/pi/runner.js +0 -76
- package/dist/session/common/catch-up.d.ts +0 -12
- package/dist/session/common/catch-up.js +0 -31
|
@@ -5,14 +5,17 @@
|
|
|
5
5
|
* Flow per run: acquire persistent connection → thread/start or thread/resume
|
|
6
6
|
* → turn/start → consume notifications (agent message deltas, approval server
|
|
7
7
|
* requests, …) until turn/completed → synthesize ResultEvent.
|
|
8
|
+
*
|
|
9
|
+
* Shares the ConnectionBasedRunner base with kimi/opencode: same notification
|
|
10
|
+
* queue + waitResolve pump, forceFinish/stopRequested semantics, turn idle
|
|
11
|
+
* watchdog, and ConnectionLostError respawn-and-retry-once.
|
|
8
12
|
*/
|
|
9
|
-
import { ConnectionManager } from '
|
|
10
|
-
import { ConnectionLostError } from './client.js';
|
|
13
|
+
import { ConnectionManager, } from '../../common/jsonrpc/connection-manager.js';
|
|
11
14
|
import { CodexAppServerTranslator } from './translator.js';
|
|
15
|
+
import { RpcErrorCode } from '../../common/acp/protocol-types.js';
|
|
16
|
+
import { mapAnswersByIndex } from '../../question-common.js';
|
|
12
17
|
import { getLogger } from '../../../logger/index.js';
|
|
13
|
-
import {
|
|
14
|
-
/** How long to wait for turn output notifications before failing. */
|
|
15
|
-
const TURN_IDLE_TIMEOUT_MS = 10 * 60 * 1000;
|
|
18
|
+
import { ConnectionBasedRunner } from '../../common/connection-based-runner.js';
|
|
16
19
|
/**
|
|
17
20
|
* Build the protocol response body for a command/file approval decision.
|
|
18
21
|
* Structured decisions (acceptWithExecpolicyAmendment) carry the payload the
|
|
@@ -32,194 +35,154 @@ function buildApprovalResponse(action, view) {
|
|
|
32
35
|
}
|
|
33
36
|
return { decision: action };
|
|
34
37
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
/**
|
|
39
|
+
* 构造 request_user_input 响应。answer → 按问题 id 回编;decline/cancel →
|
|
40
|
+
* 空 answers。answers 值与问题 options 无强校验(Codex 接受任意字符串,
|
|
41
|
+
* 包括自由文本题的自定义答案)。
|
|
42
|
+
*/
|
|
43
|
+
function buildRequestUserInputResponse(action, questions, response) {
|
|
44
|
+
if (action !== 'answer') {
|
|
45
|
+
return { answers: {} };
|
|
46
|
+
}
|
|
47
|
+
const answers = response.answers;
|
|
48
|
+
if (!answers) {
|
|
49
|
+
return { answers: {} };
|
|
50
|
+
}
|
|
51
|
+
const notes = response.notes;
|
|
52
|
+
const byIndex = mapAnswersByIndex(questions ?? [], answers);
|
|
53
|
+
const result = {};
|
|
54
|
+
(questions ?? []).forEach((q, i) => {
|
|
55
|
+
const value = byIndex[i];
|
|
56
|
+
if (!q.id || value === undefined)
|
|
57
|
+
return;
|
|
58
|
+
const values = Array.isArray(value) ? value : [value];
|
|
59
|
+
const cleaned = values.filter((v) => typeof v === 'string' && v.length > 0);
|
|
60
|
+
// Codex user_note:补充说明编码为 "user_note: <text>" 条目(2026-08-18
|
|
61
|
+
// live 验证 deepseek-v4-flash 明确理解并遵守该格式)。
|
|
62
|
+
const note = notes?.[q.question]?.trim();
|
|
63
|
+
if (note)
|
|
64
|
+
cleaned.push(`user_note: ${note}`);
|
|
65
|
+
if (cleaned.length === 0)
|
|
66
|
+
return;
|
|
67
|
+
result[q.id] = { answers: cleaned };
|
|
68
|
+
});
|
|
69
|
+
return { answers: result };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Map the client-side SandboxMode enum to the response-side SandboxPolicy
|
|
73
|
+
* object used by `thread/settings/update` (`sandboxPolicy`).
|
|
74
|
+
*/
|
|
75
|
+
export function sandboxModeToSandboxPolicy(mode) {
|
|
76
|
+
switch (mode) {
|
|
77
|
+
case 'read-only':
|
|
78
|
+
return { type: 'readOnly', networkAccess: false };
|
|
79
|
+
case 'workspace-write':
|
|
80
|
+
return {
|
|
81
|
+
type: 'workspaceWrite',
|
|
82
|
+
writableRoots: [],
|
|
83
|
+
networkAccess: false,
|
|
84
|
+
excludeTmpdirEnvVar: false,
|
|
85
|
+
excludeSlashTmp: false,
|
|
86
|
+
};
|
|
87
|
+
case 'danger-full-access':
|
|
88
|
+
return { type: 'dangerFullAccess' };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Codex 官方 feature override(thread/start·resume 的 `config` 字段):
|
|
93
|
+
* 让 `request_user_input` 在 Default 协作模式下也可用,无需改 codex
|
|
94
|
+
* config.toml。协议键与 openai/codex 的 feature registry 一致
|
|
95
|
+
* (key `default_mode_request_user_input`),app-server 官方测试
|
|
96
|
+
* (turn_start.rs)验证同一路径。
|
|
97
|
+
*/
|
|
98
|
+
export const THREAD_CONFIG_DEFAULT_MODE_REQUEST_USER_INPUT = {
|
|
99
|
+
'features.default_mode_request_user_input': true,
|
|
100
|
+
};
|
|
101
|
+
export class CodexAppServerRunner extends ConnectionBasedRunner {
|
|
39
102
|
connectionManager;
|
|
40
|
-
currentClient = null;
|
|
41
103
|
currentTranslator = null;
|
|
42
|
-
|
|
43
|
-
currentTurnId = null;
|
|
44
|
-
lastEventAt = 0;
|
|
45
|
-
_isRunning = false;
|
|
104
|
+
activeThreadId = null;
|
|
46
105
|
model;
|
|
47
106
|
modelProvider;
|
|
48
107
|
reasoningEffort;
|
|
49
108
|
sandboxConfig;
|
|
50
109
|
approvalPolicyConfig;
|
|
51
|
-
turnTimeoutMs;
|
|
52
110
|
/** Pending approval requests: requestId (JSON-RPC id) → kind + view. */
|
|
53
111
|
pendingApprovals = new Map();
|
|
54
|
-
/** Notification queue consumed by the active run generator. */
|
|
55
|
-
notificationQueue = [];
|
|
56
|
-
waitResolve = null;
|
|
57
|
-
forceFinish = false;
|
|
58
|
-
/** stop() 在 turn/start 响应前到达时置位,turn/start 完成后补发 interrupt。 */
|
|
59
|
-
stopRequested = false;
|
|
60
112
|
constructor(opts) {
|
|
61
|
-
|
|
62
|
-
this.sessionReader = opts.sessionReader;
|
|
113
|
+
super({ kind: opts.kind, sessionReader: opts.sessionReader }, opts.turnTimeoutMs);
|
|
63
114
|
this.model = opts.model;
|
|
64
115
|
this.modelProvider = opts.modelProvider;
|
|
65
116
|
this.reasoningEffort = opts.reasoningEffort;
|
|
66
117
|
this.sandboxConfig = opts.sandbox;
|
|
67
118
|
this.approvalPolicyConfig = opts.approvalPolicy;
|
|
68
|
-
this.turnTimeoutMs = opts.turnTimeoutMs ?? TURN_IDLE_TIMEOUT_MS;
|
|
69
119
|
const managerOpts = {
|
|
70
120
|
binary: opts.binary ?? 'codex',
|
|
71
|
-
args: opts.appServerArgs,
|
|
121
|
+
args: opts.appServerArgs ?? ['app-server', '--stdio'],
|
|
72
122
|
env: opts.env,
|
|
73
123
|
requestTimeoutMs: opts.requestTimeoutMs,
|
|
74
124
|
idleTtlMs: opts.idleTtlMs,
|
|
125
|
+
// `experimentalApi: true` is required for `thread/settings/update`, which
|
|
126
|
+
// lets config.save push approval/sandbox changes to a live thread instead
|
|
127
|
+
// of waiting for the workspace-lifetime runner to be recreated.
|
|
128
|
+
initializeParams: {
|
|
129
|
+
clientInfo: {
|
|
130
|
+
name: 'lark-remote',
|
|
131
|
+
version: '1.0.0',
|
|
132
|
+
},
|
|
133
|
+
capabilities: {
|
|
134
|
+
experimentalApi: true,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
75
137
|
};
|
|
76
138
|
this.connectionManager = new ConnectionManager(managerOpts);
|
|
77
139
|
}
|
|
78
|
-
get
|
|
79
|
-
return
|
|
140
|
+
get logTag() {
|
|
141
|
+
return 'codex-app-server-runner';
|
|
80
142
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
143
|
+
get turnTimeoutErrorMessage() {
|
|
144
|
+
return 'Codex app-server turn timed out';
|
|
145
|
+
}
|
|
146
|
+
get turnInterruptedErrorMessage() {
|
|
147
|
+
return 'Codex app-server turn interrupted';
|
|
148
|
+
}
|
|
149
|
+
currentSessionId() {
|
|
150
|
+
return this.activeThreadId;
|
|
151
|
+
}
|
|
152
|
+
shouldDeferStop() {
|
|
153
|
+
return !this.currentTurnId;
|
|
154
|
+
}
|
|
155
|
+
async cancelCurrentTurn() {
|
|
156
|
+
if (!this.currentClient || !this.activeThreadId || !this.currentTurnId)
|
|
157
|
+
return;
|
|
94
158
|
try {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// 连接在 setup 期间丢失(进程被误杀/异常退出):重拉进程并重试一次。
|
|
100
|
-
// thread/resume 会按持久化记录(rollout)恢复线程,继续原会话处理。
|
|
101
|
-
if (err instanceof ConnectionLostError && !this.stopRequested) {
|
|
102
|
-
getLogger().warn(`[codex-app-server-runner] connection lost during turn setup (${err.message}), respawning app-server and retrying once`);
|
|
103
|
-
// 旧连接可能仍挂在 slot 里(close 事件尚未处理完):先强制释放,
|
|
104
|
-
// 保证重试会拉起全新进程;并摘掉旧 client 的 hooks,避免其残留的
|
|
105
|
-
// onClose 把错误结果塞进重试后的通知队列。
|
|
106
|
-
await this.connectionManager.release(opts.cwd);
|
|
107
|
-
const staleClient = this.currentClient;
|
|
108
|
-
if (staleClient) {
|
|
109
|
-
staleClient.setHooks({
|
|
110
|
-
onNotification: () => { },
|
|
111
|
-
onServerRequest: () => { },
|
|
112
|
-
onClose: () => { },
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
this.resetRunState();
|
|
116
|
-
await this.setupTurn(message, opts);
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
throw err;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
if (this.stopRequested) {
|
|
123
|
-
// 启动期 /stop:turn/start 已完成,补发 interrupt,避免 server 端 turn
|
|
124
|
-
// 无人接管继续执行。
|
|
125
|
-
getLogger().warn('[codex-app-server-runner] stop requested before turn/start resolved');
|
|
126
|
-
try {
|
|
127
|
-
await this.currentClient?.request('turn/interrupt', {
|
|
128
|
-
threadId: this.currentThreadId,
|
|
129
|
-
turnId: this.currentTurnId,
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
catch (err) {
|
|
133
|
-
getLogger().warn(`[codex-app-server-runner] deferred interrupt failed: ${err.message}`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
// §9.22 守卫前提:桥的 pre-init result guard 和 run-state reducer 都以
|
|
137
|
-
// system.init 作为「本轮真实开始」的标记,而 app-server 协议没有 init 事件。
|
|
138
|
-
// 缺了它,成功的 result 会被当作 pre-init 丢弃,卡片终态停在 running,
|
|
139
|
-
// 最后兜底成「输出流已结束,但未收到 result 事件」(2026-08-13 事故)。
|
|
140
|
-
// 与 exec runner 的 syntheticInitEvent 模式一致:turn setup 成功即发 init,
|
|
141
|
-
// 保证 turn_started / turn_diff / result 全部落在 init 之后。
|
|
142
|
-
yield syntheticInitEvent(this.currentThreadId ?? opts.sessionId ?? '');
|
|
143
|
-
yield* this.consumeTurn();
|
|
159
|
+
await this.currentClient.request('turn/interrupt', {
|
|
160
|
+
threadId: this.activeThreadId,
|
|
161
|
+
turnId: this.currentTurnId,
|
|
162
|
+
});
|
|
144
163
|
}
|
|
145
164
|
catch (err) {
|
|
146
|
-
getLogger().
|
|
147
|
-
// 与 spawn 失败路径(spawning-runner §9.22)同理:error result 前必须补
|
|
148
|
-
// init,否则守卫把错误结果丢弃,卡片只显示通用「输出流已结束」而非具体
|
|
149
|
-
// 错误信息。
|
|
150
|
-
yield syntheticInitEvent(this.currentThreadId ?? opts.sessionId ?? '');
|
|
151
|
-
yield {
|
|
152
|
-
type: 'result',
|
|
153
|
-
subtype: 'error',
|
|
154
|
-
// 优先上报本次 setup 创建/恢复的线程 id:thread/start 成功后 turn/start
|
|
155
|
-
// 失败时 opts.sessionId 仍是旧值,漏报会导致下条消息再开一个孤儿线程
|
|
156
|
-
// (review P3-10)。
|
|
157
|
-
session_id: this.currentThreadId ?? opts.sessionId ?? '',
|
|
158
|
-
errorMessage: err.message,
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
finally {
|
|
162
|
-
this._isRunning = false;
|
|
163
|
-
this.currentTranslator = null;
|
|
164
|
-
this.currentTurnId = null;
|
|
165
|
-
this.stopRequested = false;
|
|
166
|
-
this.currentThreadId = null;
|
|
167
|
-
// 无论成功失败都要重新武装 idle TTL(无 slot 时是 no-op)。
|
|
168
|
-
this.connectionManager.notifyIdle(opts.cwd);
|
|
165
|
+
getLogger().warn(`[${this.logTag}] interrupt failed: ${err.message}`);
|
|
169
166
|
}
|
|
170
167
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
async
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
getLogger().warn('[codex-app-server-runner] client connection closed');
|
|
184
|
-
this.failTurn('Codex app-server connection closed');
|
|
185
|
-
},
|
|
186
|
-
});
|
|
187
|
-
const threadParams = this.buildThreadParams(opts.cwd);
|
|
188
|
-
let threadId;
|
|
189
|
-
if (opts.sessionId) {
|
|
190
|
-
const resumeParams = {
|
|
191
|
-
threadId: opts.sessionId,
|
|
192
|
-
...threadParams,
|
|
193
|
-
};
|
|
194
|
-
await client.request('thread/resume', resumeParams);
|
|
195
|
-
threadId = opts.sessionId;
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
const threadResult = await client.request('thread/start', threadParams);
|
|
199
|
-
// 会话键假设(review P2-4):主线程 thread.id === session_meta.session_id,
|
|
200
|
-
// 故 thread.id 可同时用作协议 threadId 与 store/session reader 的 session
|
|
201
|
-
// 键(bridge 用 turn_started 通知的 threadId 写回,/resume、Compact 按此
|
|
202
|
-
// 键定位 JSONL)。forked/subagent 线程二者会分叉(openai/codex#29327),
|
|
203
|
-
// 桥只把主线程作为顶层会话,不在此链路内。
|
|
204
|
-
threadId = threadResult.thread.id;
|
|
205
|
-
}
|
|
206
|
-
this.currentThreadId = threadId;
|
|
207
|
-
const translator = new CodexAppServerTranslator();
|
|
208
|
-
this.currentTranslator = translator;
|
|
209
|
-
const turnParams = this.buildTurnParams(message, opts);
|
|
210
|
-
const turnResult = await client.request('turn/start', turnParams);
|
|
211
|
-
this.currentTurnId = turnResult.turn.id;
|
|
168
|
+
clearTurnState() {
|
|
169
|
+
this.currentTranslator = null;
|
|
170
|
+
this.activeThreadId = null;
|
|
171
|
+
}
|
|
172
|
+
async releaseConnection(cwd) {
|
|
173
|
+
await this.connectionManager.release(cwd);
|
|
174
|
+
}
|
|
175
|
+
notifyIdle(cwd) {
|
|
176
|
+
this.connectionManager.notifyIdle(cwd);
|
|
177
|
+
}
|
|
178
|
+
async disposeConnections() {
|
|
179
|
+
await this.connectionManager.disposeAll();
|
|
212
180
|
}
|
|
213
181
|
/**
|
|
214
182
|
* Run a compact operation on the current thread.
|
|
215
183
|
*/
|
|
216
184
|
async *runCompact(_message, opts) {
|
|
217
|
-
|
|
218
|
-
throw new Error('CodexAppServerRunner is already running');
|
|
219
|
-
}
|
|
220
|
-
this._isRunning = true;
|
|
221
|
-
this.resetRunState();
|
|
222
|
-
try {
|
|
185
|
+
yield* this.executeTurn(opts, async () => {
|
|
223
186
|
const client = await this.connectionManager.acquire(opts.cwd);
|
|
224
187
|
this.connectionManager.notifyActivity(opts.cwd);
|
|
225
188
|
this.currentClient = client;
|
|
@@ -231,7 +194,7 @@ export class CodexAppServerRunner {
|
|
|
231
194
|
if (!opts.sessionId) {
|
|
232
195
|
throw new Error('compact requires a sessionId');
|
|
233
196
|
}
|
|
234
|
-
this.
|
|
197
|
+
this.activeThreadId = opts.sessionId;
|
|
235
198
|
const translator = new CodexAppServerTranslator();
|
|
236
199
|
translator.setOperationKind('compact');
|
|
237
200
|
this.currentTranslator = translator;
|
|
@@ -246,55 +209,7 @@ export class CodexAppServerRunner {
|
|
|
246
209
|
};
|
|
247
210
|
await client.request('thread/resume', resumeParams);
|
|
248
211
|
await client.request('thread/compact/start', { threadId: opts.sessionId });
|
|
249
|
-
|
|
250
|
-
yield syntheticInitEvent(this.currentThreadId ?? opts.sessionId ?? '');
|
|
251
|
-
yield* this.consumeTurn();
|
|
252
|
-
}
|
|
253
|
-
catch (err) {
|
|
254
|
-
getLogger().error(`[codex-app-server-runner] runCompact error: ${err.message}`);
|
|
255
|
-
yield syntheticInitEvent(this.currentThreadId ?? opts.sessionId ?? '');
|
|
256
|
-
yield {
|
|
257
|
-
type: 'result',
|
|
258
|
-
subtype: 'error',
|
|
259
|
-
session_id: this.currentThreadId ?? opts.sessionId ?? '',
|
|
260
|
-
errorMessage: err.message,
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
finally {
|
|
264
|
-
this._isRunning = false;
|
|
265
|
-
this.currentTranslator = null;
|
|
266
|
-
this.currentTurnId = null;
|
|
267
|
-
this.stopRequested = false;
|
|
268
|
-
this.currentThreadId = null;
|
|
269
|
-
this.connectionManager.notifyIdle(opts.cwd);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Stop the current turn by interrupting it.
|
|
274
|
-
*/
|
|
275
|
-
async stop(_opts) {
|
|
276
|
-
if (!this._isRunning || !this.currentClient || !this.currentThreadId)
|
|
277
|
-
return;
|
|
278
|
-
if (!this.currentTurnId) {
|
|
279
|
-
// turn/start 尚未响应:先标记,等 run() 在 turn/start 完成后补发 interrupt。
|
|
280
|
-
this.stopRequested = true;
|
|
281
|
-
this.forceFinish = true;
|
|
282
|
-
this.wakeWaiters();
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
try {
|
|
286
|
-
await this.currentClient.request('turn/interrupt', {
|
|
287
|
-
threadId: this.currentThreadId,
|
|
288
|
-
turnId: this.currentTurnId ?? '',
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
catch (err) {
|
|
292
|
-
getLogger().warn(`[codex-app-server-runner] stop error: ${err.message}`);
|
|
293
|
-
}
|
|
294
|
-
// Unblock the turn loop regardless of whether the server sends a
|
|
295
|
-
// turn/completed notification afterwards.
|
|
296
|
-
this.forceFinish = true;
|
|
297
|
-
this.wakeWaiters();
|
|
212
|
+
});
|
|
298
213
|
}
|
|
299
214
|
/**
|
|
300
215
|
* Respond to an approval server request. `response` is the bridge
|
|
@@ -306,7 +221,15 @@ export class CodexAppServerRunner {
|
|
|
306
221
|
if (!client || !pending)
|
|
307
222
|
return;
|
|
308
223
|
const action = response?.action ?? 'decline';
|
|
309
|
-
if (pending.kind === '
|
|
224
|
+
if (pending.kind === 'question') {
|
|
225
|
+
// AskUserQuestion 回编:文本 key 答案按问题顺序展开后映射回问题 id
|
|
226
|
+
// ({answers:{id:{answers:[值]}}});跳过/拒绝/取消 → 空 answers
|
|
227
|
+
// (Codex 视为 unanswered,turn 继续,对齐 cc-connect deny 语义)。
|
|
228
|
+
const questions = pending.view.questions ?? [];
|
|
229
|
+
const answerPayload = buildRequestUserInputResponse(action, questions, response);
|
|
230
|
+
client.respond(requestId, answerPayload);
|
|
231
|
+
}
|
|
232
|
+
else if (pending.kind === 'permissions') {
|
|
310
233
|
// 权限审批的响应没有 decision 字段(真实协议只回 granted profile +
|
|
311
234
|
// scope):decline/cancel 必须返回空授权(拒绝全部),否则会把用户已
|
|
312
235
|
// 勾选的条目当作授予返回(2026-08-12 review 发现:勾选后点「拒绝」实
|
|
@@ -323,30 +246,7 @@ export class CodexAppServerRunner {
|
|
|
323
246
|
client.respond(requestId, buildApprovalResponse(action, pending.view));
|
|
324
247
|
}
|
|
325
248
|
this.pendingApprovals.delete(requestId);
|
|
326
|
-
getLogger().info(`[
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* Dispose the runner: release the connection.
|
|
330
|
-
*/
|
|
331
|
-
async dispose() {
|
|
332
|
-
try {
|
|
333
|
-
await this.connectionManager.disposeAll();
|
|
334
|
-
}
|
|
335
|
-
catch (err) {
|
|
336
|
-
getLogger().warn(`[codex-app-server-runner] dispose error: ${err.message}`);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
getUsageAuthority() {
|
|
340
|
-
return 'live';
|
|
341
|
-
}
|
|
342
|
-
killOrphan() {
|
|
343
|
-
// No-op: the app-server connection is managed by ConnectionManager.
|
|
344
|
-
}
|
|
345
|
-
registerExitHandlers() {
|
|
346
|
-
// No-op: connection manager handles cleanup on exit.
|
|
347
|
-
}
|
|
348
|
-
unregisterExitHandlers() {
|
|
349
|
-
// No-op.
|
|
249
|
+
getLogger().info(`[${this.logTag}] approval responded requestId=${requestId} kind=${pending.kind} action=${action}`);
|
|
350
250
|
}
|
|
351
251
|
getStatusInfo() {
|
|
352
252
|
return {
|
|
@@ -361,6 +261,45 @@ export class CodexAppServerRunner {
|
|
|
361
261
|
},
|
|
362
262
|
};
|
|
363
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* Unified approval-mode hot-update entry (called by the bridge via duck
|
|
266
|
+
* typing on active runs). Codex's `thread/settings/update` applies the new
|
|
267
|
+
* approval/sandbox policy to subsequent turns, even while the current turn
|
|
268
|
+
* is still active. This lets `/config` take effect without waiting for this
|
|
269
|
+
* workspace-lifetime runner to be recreated/evicted. Local fields are always
|
|
270
|
+
* updated so `/status` reflects the new config immediately; if there is no
|
|
271
|
+
* live thread, the next `thread/start`/`thread/resume` uses the new values
|
|
272
|
+
* anyway.
|
|
273
|
+
*/
|
|
274
|
+
async updateApprovalMode(settings) {
|
|
275
|
+
if (settings.approvalPolicy !== undefined) {
|
|
276
|
+
this.approvalPolicyConfig = settings.approvalPolicy;
|
|
277
|
+
}
|
|
278
|
+
if (settings.sandbox !== undefined) {
|
|
279
|
+
this.sandboxConfig = settings.sandbox;
|
|
280
|
+
}
|
|
281
|
+
if (!this.currentClient || !this.activeThreadId) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
const params = {
|
|
285
|
+
threadId: this.activeThreadId,
|
|
286
|
+
...(settings.approvalPolicy !== undefined ? { approvalPolicy: settings.approvalPolicy } : {}),
|
|
287
|
+
...(settings.sandbox !== undefined
|
|
288
|
+
? { sandboxPolicy: sandboxModeToSandboxPolicy(settings.sandbox) }
|
|
289
|
+
: {}),
|
|
290
|
+
};
|
|
291
|
+
try {
|
|
292
|
+
await this.currentClient.request('thread/settings/update', params);
|
|
293
|
+
getLogger().info(`[${this.logTag}] thread/settings/update applied for thread=${this.activeThreadId}`);
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
// Config save must not fail because a live thread rejected an experimental
|
|
297
|
+
// update (e.g. older Codex without thread/settings/update). The local
|
|
298
|
+
// snapshot is already updated, so /status and the next thread start/resume
|
|
299
|
+
// still use the new values.
|
|
300
|
+
getLogger().warn(`[${this.logTag}] thread/settings/update failed: ${err.message}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
364
303
|
// =========================================================================
|
|
365
304
|
// Internal
|
|
366
305
|
// =========================================================================
|
|
@@ -371,12 +310,15 @@ export class CodexAppServerRunner {
|
|
|
371
310
|
...(this.modelProvider ? { modelProvider: this.modelProvider } : {}),
|
|
372
311
|
...(this.approvalPolicyConfig ? { approvalPolicy: this.approvalPolicyConfig } : {}),
|
|
373
312
|
...(this.sandboxConfig ? { sandbox: this.sandboxConfig } : {}),
|
|
313
|
+
// Default 协作模式下默认启用 request_user_input(Ask User Question),
|
|
314
|
+
// 协议级 override,不依赖 codex config.toml。
|
|
315
|
+
config: THREAD_CONFIG_DEFAULT_MODE_REQUEST_USER_INPUT,
|
|
374
316
|
};
|
|
375
317
|
return params;
|
|
376
318
|
}
|
|
377
319
|
buildTurnParams(message, opts) {
|
|
378
320
|
const params = {
|
|
379
|
-
threadId: this.
|
|
321
|
+
threadId: this.activeThreadId ?? '',
|
|
380
322
|
input: [{ type: 'text', text: message }],
|
|
381
323
|
...((opts.model ?? this.model) ? { model: opts.model ?? this.model } : {}),
|
|
382
324
|
...((opts.reasoningEffort ?? this.reasoningEffort)
|
|
@@ -386,79 +328,46 @@ export class CodexAppServerRunner {
|
|
|
386
328
|
return params;
|
|
387
329
|
}
|
|
388
330
|
/**
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*/
|
|
392
|
-
async *consumeTurn() {
|
|
393
|
-
while (true) {
|
|
394
|
-
const timedOut = await this.waitForEvents(this.nextTurnIdleDeadline());
|
|
395
|
-
if (timedOut) {
|
|
396
|
-
await this.interruptCurrentTurn();
|
|
397
|
-
getLogger().warn('[codex-app-server-runner] turn wait timed out');
|
|
398
|
-
yield {
|
|
399
|
-
type: 'result',
|
|
400
|
-
subtype: 'error',
|
|
401
|
-
session_id: this.currentThreadId ?? '',
|
|
402
|
-
errorMessage: 'Codex app-server turn timed out',
|
|
403
|
-
};
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
if (this.forceFinish) {
|
|
407
|
-
yield {
|
|
408
|
-
type: 'result',
|
|
409
|
-
subtype: 'interrupted',
|
|
410
|
-
session_id: this.currentThreadId ?? '',
|
|
411
|
-
errorMessage: 'Codex app-server turn interrupted',
|
|
412
|
-
};
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
while (this.notificationQueue.length > 0) {
|
|
416
|
-
const ev = this.notificationQueue.shift();
|
|
417
|
-
yield ev;
|
|
418
|
-
if (ev.type === 'result') {
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
/**
|
|
425
|
-
* Compute the current idle deadline. 0 disables the idle timeout; otherwise
|
|
426
|
-
* the deadline is based on the last received event, not the turn start time.
|
|
331
|
+
* Acquire the connection and set up the turn: thread/start(新会话)或
|
|
332
|
+
* thread/resume(按 sessionId 恢复既有线程)→ turn/start。
|
|
427
333
|
*/
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
334
|
+
async setupTurn(message, opts) {
|
|
335
|
+
const client = await this.connectionManager.acquire(opts.cwd);
|
|
336
|
+
this.connectionManager.notifyActivity(opts.cwd);
|
|
337
|
+
this.currentClient = client;
|
|
338
|
+
client.setHooks({
|
|
339
|
+
onNotification: (method, params) => this.handleNotification(method, params),
|
|
340
|
+
onServerRequest: (id, method, params) => this.handleServerRequest(id, method, params),
|
|
341
|
+
onClose: () => {
|
|
342
|
+
getLogger().warn(`[${this.logTag}] client connection closed`);
|
|
343
|
+
this.failTurn('Codex app-server connection closed');
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
const threadParams = this.buildThreadParams(opts.cwd);
|
|
347
|
+
let threadId;
|
|
348
|
+
if (opts.sessionId) {
|
|
349
|
+
const resumeParams = {
|
|
350
|
+
threadId: opts.sessionId,
|
|
351
|
+
...threadParams,
|
|
352
|
+
};
|
|
353
|
+
await client.request('thread/resume', resumeParams);
|
|
354
|
+
threadId = opts.sessionId;
|
|
444
355
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
setTimeout(() => {
|
|
454
|
-
if (this.waitResolve === resolve) {
|
|
455
|
-
this.waitResolve = null;
|
|
456
|
-
resolve();
|
|
457
|
-
}
|
|
458
|
-
}, Math.min(remaining, 30_000));
|
|
459
|
-
});
|
|
356
|
+
else {
|
|
357
|
+
const threadResult = await client.request('thread/start', threadParams);
|
|
358
|
+
// 会话键假设(review P2-4):主线程 thread.id === session_meta.session_id,
|
|
359
|
+
// 故 thread.id 可同时用作协议 threadId 与 store/session reader 的 session
|
|
360
|
+
// 键(bridge 用 turn_started 通知的 threadId 写回,/resume、Compact 按此
|
|
361
|
+
// 键定位 JSONL)。forked/subagent 线程二者会分叉(openai/codex#29327),
|
|
362
|
+
// 桥只把主线程作为顶层会话,不在此链路内。
|
|
363
|
+
threadId = threadResult.thread.id;
|
|
460
364
|
}
|
|
461
|
-
|
|
365
|
+
this.activeThreadId = threadId;
|
|
366
|
+
const translator = new CodexAppServerTranslator();
|
|
367
|
+
this.currentTranslator = translator;
|
|
368
|
+
const turnParams = this.buildTurnParams(message, opts);
|
|
369
|
+
const turnResult = await client.request('turn/start', turnParams);
|
|
370
|
+
this.currentTurnId = turnResult.turn.id;
|
|
462
371
|
}
|
|
463
372
|
handleNotification(method, params) {
|
|
464
373
|
const events = this.currentTranslator?.handleNotification(method, params) ?? [];
|
|
@@ -470,48 +379,17 @@ export class CodexAppServerRunner {
|
|
|
470
379
|
// 未处理/不支持的服务端请求必须显式响应(error 即"拒绝",turn 继续),
|
|
471
380
|
// 否则服务端会一直等待响应(applyPatchApproval、item/tool/requestUserInput、
|
|
472
381
|
// mcpServer/elicitation/request 等)。
|
|
473
|
-
this.currentClient?.respondError(id,
|
|
382
|
+
this.currentClient?.respondError(id, RpcErrorCode.METHOD_NOT_FOUND, `Unsupported server request: ${method}`);
|
|
474
383
|
return;
|
|
475
384
|
}
|
|
476
385
|
for (const ev of events) {
|
|
477
386
|
if (ev.type === 'approval_requested') {
|
|
478
387
|
this.pendingApprovals.set(ev.requestId, { kind: ev.kind, view: ev.view });
|
|
479
|
-
getLogger().info(`[
|
|
388
|
+
getLogger().info(`[${this.logTag}] approval requested requestId=${ev.requestId} kind=${ev.kind}`);
|
|
480
389
|
}
|
|
481
390
|
}
|
|
482
391
|
this.pushEvents(events);
|
|
483
392
|
}
|
|
484
|
-
pushEvents(events) {
|
|
485
|
-
if (events.length === 0)
|
|
486
|
-
return;
|
|
487
|
-
this.lastEventAt = Date.now();
|
|
488
|
-
this.notificationQueue.push(...events);
|
|
489
|
-
this.wakeWaiters();
|
|
490
|
-
}
|
|
491
|
-
wakeWaiters() {
|
|
492
|
-
if (this.waitResolve) {
|
|
493
|
-
const resolve = this.waitResolve;
|
|
494
|
-
this.waitResolve = null;
|
|
495
|
-
resolve();
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
failTurn(message) {
|
|
499
|
-
this.pushEvents([
|
|
500
|
-
{
|
|
501
|
-
type: 'result',
|
|
502
|
-
subtype: 'error',
|
|
503
|
-
session_id: this.currentThreadId ?? '',
|
|
504
|
-
errorMessage: message,
|
|
505
|
-
},
|
|
506
|
-
]);
|
|
507
|
-
}
|
|
508
|
-
resetRunState() {
|
|
509
|
-
this.notificationQueue = [];
|
|
510
|
-
this.waitResolve = null;
|
|
511
|
-
this.lastEventAt = Date.now();
|
|
512
|
-
this.forceFinish = false;
|
|
513
|
-
this.pendingApprovals.clear();
|
|
514
|
-
}
|
|
515
393
|
buildGrantedPermissions(view) {
|
|
516
394
|
const items = view.permissions?.items ?? [];
|
|
517
395
|
const read = [];
|