@co0ontty/wand 4.93.0 → 4.94.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -30
- package/dist/ai-text-runner.d.ts +2 -10
- package/dist/ai-text-runner.js +13 -74
- package/dist/build-info.json +3 -3
- package/dist/cli.js +47 -9
- package/dist/config.d.ts +2 -3
- package/dist/config.js +26 -5
- package/dist/core-status-cli.d.ts +1 -1
- package/dist/core-status-cli.js +3 -1
- package/dist/daemon-connection.d.ts +10 -0
- package/dist/daemon-connection.js +28 -0
- package/dist/diagnostic-logs.d.ts +10 -0
- package/dist/diagnostic-logs.js +121 -0
- package/dist/employee-text.js +1 -3
- package/dist/git-quick-commit.d.ts +1 -1
- package/dist/git-quick-commit.js +2 -9
- package/dist/layout-tree.d.ts +3 -1
- package/dist/layout-tree.js +8 -0
- package/dist/local-model-process.js +20 -0
- package/dist/local-model-setup.js +5 -3
- package/dist/managed-temp.d.ts +32 -0
- package/dist/managed-temp.js +176 -0
- package/dist/middleware/api-scope.js +1 -0
- package/dist/models/shared.d.ts +0 -6
- package/dist/models/shared.js +0 -12
- package/dist/models/thinking.d.ts +0 -2
- package/dist/models/thinking.js +0 -2
- package/dist/models.d.ts +0 -1
- package/dist/models.js +3 -11
- package/dist/native-session-title.d.ts +1 -1
- package/dist/native-session-title.js +1 -1
- package/dist/pi-model-discovery.js +5 -17
- package/dist/pi-resource-recommendation.d.ts +1 -1
- package/dist/pi-resource-recommendation.js +129 -42
- package/dist/process-manager-native-history.js +1 -6
- package/dist/process-manager.d.ts +0 -1
- package/dist/process-manager.js +5 -18
- package/dist/provider-catalog.d.ts +1 -2
- package/dist/provider-catalog.js +1 -6
- package/dist/provider-cli-updater.js +1 -9
- package/dist/provider-history-scanner.d.ts +0 -3
- package/dist/provider-history-scanner.js +0 -26
- package/dist/render-daemon-client.js +6 -29
- package/dist/render-structured-client.js +2 -7
- package/dist/retention.js +1 -6
- package/dist/run-command.d.ts +5 -0
- package/dist/run-command.js +14 -1
- package/dist/server-session-lifecycle-routes.js +1 -1
- package/dist/server-storage-cleanup-routes.d.ts +3 -0
- package/dist/server-storage-cleanup-routes.js +19 -0
- package/dist/server-workspace-routes.d.ts +4 -1
- package/dist/server-workspace-routes.js +75 -9
- package/dist/server.js +21 -16
- package/dist/service-command-privilege.d.ts +17 -0
- package/dist/service-command-privilege.js +55 -0
- package/dist/service-manager/index.d.ts +3 -0
- package/dist/service-manager/index.js +3 -0
- package/dist/service-manager/launchd.d.ts +1 -0
- package/dist/service-manager/launchd.js +15 -0
- package/dist/service-manager/systemd.d.ts +1 -0
- package/dist/service-manager/systemd.js +11 -0
- package/dist/session-ai-context.js +17 -6
- package/dist/session-completion-state.d.ts +2 -0
- package/dist/session-completion-state.js +6 -2
- package/dist/session-logger.js +7 -1
- package/dist/session-model.d.ts +4 -0
- package/dist/session-model.js +13 -4
- package/dist/session-registry.d.ts +1 -1
- package/dist/session-registry.js +0 -3
- package/dist/silicon-employee-draft.js +0 -1
- package/dist/speech-service.js +18 -8
- package/dist/storage/mappers.js +6 -10
- package/dist/storage/migrations.js +14 -0
- package/dist/storage/uploads-repository.d.ts +36 -0
- package/dist/storage/uploads-repository.js +98 -0
- package/dist/storage-cleanup.d.ts +50 -0
- package/dist/storage-cleanup.js +344 -0
- package/dist/storage.d.ts +3 -1
- package/dist/storage.js +4 -0
- package/dist/structured-failure.d.ts +2 -0
- package/dist/structured-failure.js +9 -0
- package/dist/structured-grok-adapter.js +4 -4
- package/dist/structured-opencode-adapter.js +4 -9
- package/dist/structured-provider-common.d.ts +1 -2
- package/dist/structured-provider-common.js +3 -6
- package/dist/structured-session-manager-replay.js +2 -6
- package/dist/structured-session-manager.d.ts +1 -2
- package/dist/structured-session-manager.js +32 -25
- package/dist/task-organize-contract.d.ts +41 -0
- package/dist/task-organize-contract.js +4 -0
- package/dist/task-title-organize-service.d.ts +21 -0
- package/dist/task-title-organize-service.js +168 -0
- package/dist/task-title-organize.d.ts +77 -0
- package/dist/task-title-organize.js +346 -0
- package/dist/terminal-daemon-client.js +10 -48
- package/dist/time-utils.d.ts +2 -0
- package/dist/time-utils.js +7 -0
- package/dist/tui/attach.js +6 -5
- package/dist/tui/commands.d.ts +4 -1
- package/dist/tui/commands.js +36 -25
- package/dist/tui/index.js +2 -2
- package/dist/tui/service-actions.js +5 -4
- package/dist/tui/service-command.d.ts +6 -0
- package/dist/tui/service-command.js +40 -0
- package/dist/tui/service-panel.js +9 -8
- package/dist/types.d.ts +4 -4
- package/dist/upload-reference-guard.d.ts +4 -0
- package/dist/upload-reference-guard.js +29 -0
- package/dist/upload-routes.d.ts +4 -0
- package/dist/upload-routes.js +121 -21
- package/dist/user-profile.d.ts +1 -1
- package/dist/user-profile.js +8 -3
- package/dist/web-ui/content/ai-teams.js +9 -9
- package/dist/web-ui/content/module-manifest.json +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/chat-render-EODYMQF6.js → 0051bd3d55f10ab7/chat-render-RY2I2CF3.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-ZTEQSXPA.js → 0051bd3d55f10ab7/chunk-2F6J2MKX.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-A33YPCY6.js → 0051bd3d55f10ab7/chunk-7AFM245W.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-QNUWUMKW.js → 0051bd3d55f10ab7/chunk-MHX2QXIS.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-WGADUEIT.js → 0051bd3d55f10ab7/chunk-RAAPETNH.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-I3UYDQLS.js → 0051bd3d55f10ab7/chunk-RXKBHYUR.js} +2 -2
- package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-3TCUG766.js → 0051bd3d55f10ab7/chunk-U4EDP4TI.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-HK7RBU3M.js → 0051bd3d55f10ab7/chunk-WHXQR7JT.js} +1 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/employee-card-NV34TZIH.js +1 -0
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-2PU6TV2I.js → 0051bd3d55f10ab7/host-2IVIZ2YC.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-BPYNMKFC.js → 0051bd3d55f10ab7/host-AZXNFCRW.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-HZIKPWYY.js → 0051bd3d55f10ab7/host-EPEQX22R.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-YELXZID4.js → 0051bd3d55f10ab7/host-FZYIMOLC.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-OGD6AWHL.js → 0051bd3d55f10ab7/host-G6GL4SNL.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-E3SCPOIU.js → 0051bd3d55f10ab7/host-PEILC5NU.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-OWD32FZ3.js → 0051bd3d55f10ab7/host-PHHNGVNL.js} +2 -2
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-CXDAFMXX.js → 0051bd3d55f10ab7/host-Q524TMSR.js} +1 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-RO5MB6RB.js +69 -0
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-J5XRAQFP.js → 0051bd3d55f10ab7/host-WOQRK6NG.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-HKXE2DOT.js → 0051bd3d55f10ab7/host-XNIR3NTJ.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/host-4E4H4JZJ.js → 0051bd3d55f10ab7/host-XRVOIEP6.js} +1 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-ZDKOQEMS.js +1 -0
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/host-bridge-PMQZRG5G.js +1 -0
- package/dist/web-ui/content/modules/{2548dbe0d0783577/library-bridge-Y3Y24CPQ.js → 0051bd3d55f10ab7/library-bridge-DTMQIHVJ.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/page-GQBUU7T7.js → 0051bd3d55f10ab7/page-PY4JUL4W.js} +1 -1
- package/dist/web-ui/content/modules/{2548dbe0d0783577/panel-PG662OC5.js → 0051bd3d55f10ab7/panel-BBQFC55Q.js} +1 -1
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/runtime.js +974 -0
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/task-board-host-PXZSWBBG.js +2 -0
- package/dist/web-ui/content/modules/0051bd3d55f10ab7/workspace-agent-picker-DJSECSY6.js +1 -0
- package/dist/web-ui/content/modules/{2548dbe0d0783577/workspace-window-HV4T3WKR.js → 0051bd3d55f10ab7/workspace-window-MNMKSOBG.js} +1 -1
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.d.ts +27 -27
- package/dist/web-ui/embedded-assets.js +4 -4
- package/dist/web-ui/provider-identity.d.ts +4 -5
- package/dist/web-ui/provider-identity.js +4 -10
- package/dist/web-ui/react/workspaces/sidebar-manage.d.ts +39 -0
- package/dist/web-ui/react/workspaces/sidebar-manage.js +149 -0
- package/dist/web-ui/react/workspaces/types.d.ts +408 -0
- package/dist/web-ui/react/workspaces/types.js +16 -0
- package/dist/web-ui/session-activity.js +1 -1
- package/package.json +2 -2
- package/dist/structured-gemini-adapter.d.ts +0 -40
- package/dist/structured-gemini-adapter.js +0 -208
- package/dist/web-ui/content/modules/2548dbe0d0783577/chunk-XGVT4ZWT.js +0 -1
- package/dist/web-ui/content/modules/2548dbe0d0783577/employee-card-LJ3TX72B.js +0 -1
- package/dist/web-ui/content/modules/2548dbe0d0783577/host-ITV2QZ4A.js +0 -1
- package/dist/web-ui/content/modules/2548dbe0d0783577/host-XNWC7KPR.js +0 -69
- package/dist/web-ui/content/modules/2548dbe0d0783577/host-bridge-G5HEVADT.js +0 -1
- package/dist/web-ui/content/modules/2548dbe0d0783577/runtime.js +0 -946
- package/dist/web-ui/content/modules/2548dbe0d0783577/task-board-host-RAN2Z2KR.js +0 -2
- package/dist/web-ui/content/modules/2548dbe0d0783577/workspace-agent-picker-FXLH6N5K.js +0 -1
- /package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-YX475LXO.js → 0051bd3d55f10ab7/chunk-6C6A2RHG.js} +0 -0
- /package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-NP35GDQ3.js → 0051bd3d55f10ab7/chunk-ECWYA3U5.js} +0 -0
- /package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-47EVOSJS.js → 0051bd3d55f10ab7/chunk-T35D57FW.js} +0 -0
- /package/dist/web-ui/content/modules/{2548dbe0d0783577 → 0051bd3d55f10ab7}/chunk-YOXXNXOX.js +0 -0
- /package/dist/web-ui/content/modules/{2548dbe0d0783577/chunk-C3K34WJD.js → 0051bd3d55f10ab7/chunk-YQJ6XYV7.js} +0 -0
- /package/dist/web-ui/content/modules/{2548dbe0d0783577/host-J6HBDK4A.js → 0051bd3d55f10ab7/host-2AQAIUNO.js} +0 -0
- /package/dist/web-ui/content/modules/{2548dbe0d0783577 → 0051bd3d55f10ab7}/sha256-ANXV72UC.js +0 -0
|
@@ -5,7 +5,6 @@ import { isRecord } from "./object-utils.js";
|
|
|
5
5
|
const MODEL_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,127}$/;
|
|
6
6
|
const SUPPORTED_APIS = new Set([
|
|
7
7
|
"anthropic-messages",
|
|
8
|
-
"google-generative-ai",
|
|
9
8
|
"openai-completions",
|
|
10
9
|
"openai-responses",
|
|
11
10
|
]);
|
|
@@ -61,9 +60,6 @@ function endpointUrl(baseUrl, api) {
|
|
|
61
60
|
if (api === "anthropic-messages") {
|
|
62
61
|
suffix = /\/v\d+(?:beta)?$/i.test(basePath) ? "models" : "v1/models";
|
|
63
62
|
}
|
|
64
|
-
else if (api === "google-generative-ai") {
|
|
65
|
-
suffix = /\/v\d+(?:beta)?$/i.test(basePath) ? "models" : "v1beta/models";
|
|
66
|
-
}
|
|
67
63
|
else {
|
|
68
64
|
suffix = "models";
|
|
69
65
|
}
|
|
@@ -135,8 +131,6 @@ function asModelId(value, api) {
|
|
|
135
131
|
if (typeof value !== "string")
|
|
136
132
|
return "";
|
|
137
133
|
const trimmed = value.trim();
|
|
138
|
-
if (api === "google-generative-ai")
|
|
139
|
-
return trimmed.replace(/^models\//, "");
|
|
140
134
|
return trimmed;
|
|
141
135
|
}
|
|
142
136
|
function parseEndpointPage(api, body, provider) {
|
|
@@ -149,23 +143,20 @@ function parseEndpointPage(api, body, provider) {
|
|
|
149
143
|
for (const row of rows) {
|
|
150
144
|
if (!isRecord(row))
|
|
151
145
|
continue;
|
|
152
|
-
const id = asModelId(
|
|
146
|
+
const id = asModelId(row.id, api);
|
|
153
147
|
const qualifiedId = `${provider}/${id}`;
|
|
154
148
|
if (!id || id.length > 128 || !MODEL_ID_PATTERN.test(id) || !MODEL_ID_PATTERN.test(qualifiedId))
|
|
155
149
|
continue;
|
|
156
150
|
const displayName = typeof row.display_name === "string" ? row.display_name.trim()
|
|
157
151
|
: typeof row.displayName === "string" ? row.displayName.trim()
|
|
158
|
-
: typeof row.name === "string"
|
|
152
|
+
: typeof row.name === "string" ? row.name.trim() : "";
|
|
159
153
|
models.push({
|
|
160
154
|
id: qualifiedId,
|
|
161
155
|
label: displayName && displayName !== id ? `${displayName} · ${qualifiedId}` : qualifiedId,
|
|
162
156
|
});
|
|
163
157
|
}
|
|
164
158
|
let nextCursor = "";
|
|
165
|
-
if (
|
|
166
|
-
nextCursor = typeof body.nextPageToken === "string" ? body.nextPageToken : "";
|
|
167
|
-
}
|
|
168
|
-
else if (body.has_more === true) {
|
|
159
|
+
if (body.has_more === true) {
|
|
169
160
|
const lastRow = rows.at(-1);
|
|
170
161
|
const lastRowId = isRecord(lastRow) && typeof lastRow.id === "string" ? lastRow.id : "";
|
|
171
162
|
nextCursor = typeof body.last_id === "string" ? body.last_id : lastRowId;
|
|
@@ -177,8 +168,7 @@ function pageUrl(endpoint, cursor) {
|
|
|
177
168
|
if (!url)
|
|
178
169
|
return null;
|
|
179
170
|
if (cursor) {
|
|
180
|
-
const key = endpoint.api === "
|
|
181
|
-
: endpoint.api === "anthropic-messages" ? "after_id" : "after";
|
|
171
|
+
const key = endpoint.api === "anthropic-messages" ? "after_id" : "after";
|
|
182
172
|
url.searchParams.set(key, cursor);
|
|
183
173
|
}
|
|
184
174
|
return url;
|
|
@@ -214,9 +204,7 @@ async function fetchEndpointModels(endpoint, fetchImpl) {
|
|
|
214
204
|
break;
|
|
215
205
|
const headers = { ...endpoint.headers };
|
|
216
206
|
if (endpoint.apiKey) {
|
|
217
|
-
if (endpoint.api === "
|
|
218
|
-
headers["x-goog-api-key"] = endpoint.apiKey;
|
|
219
|
-
else if (endpoint.api === "anthropic-messages") {
|
|
207
|
+
if (endpoint.api === "anthropic-messages") {
|
|
220
208
|
headers["x-api-key"] = endpoint.apiKey;
|
|
221
209
|
headers["anthropic-version"] = "2023-06-01";
|
|
222
210
|
}
|
|
@@ -18,7 +18,7 @@ export declare function decidePiCodemode(input: {
|
|
|
18
18
|
caller: string;
|
|
19
19
|
signal?: AbortSignal;
|
|
20
20
|
}): Promise<PiAutomaticCodemodeChoice>;
|
|
21
|
-
/** Read-only matching.
|
|
21
|
+
/** Read-only matching. Bounded system-role decisions rank the candidate set; they never connect MCP or grant authority. */
|
|
22
22
|
export declare function recommendPiResources(input: {
|
|
23
23
|
prompt: string;
|
|
24
24
|
candidates: PiResourceSelection;
|
|
@@ -48,14 +48,6 @@ function resourceTask(prompt, catalog) {
|
|
|
48
48
|
const task = clauses.filter((clause) => !switchOnly.test(clause.trim()));
|
|
49
49
|
return task.length === clauses.length ? prompt : task.filter((clause) => clause.trim()).join(",").trim();
|
|
50
50
|
}
|
|
51
|
-
const MATCH_INSTRUCTIONS = "Which skill fits the task?";
|
|
52
|
-
/** Keep the task separate from skill marketing text; the small model overweights long descriptions. */
|
|
53
|
-
function shortlistQuestions(name) {
|
|
54
|
-
return {
|
|
55
|
-
forward: { type: "choice", instructions: MATCH_INSTRUCTIONS, criteria: { match: name, none: "None" } },
|
|
56
|
-
reverse: { type: "choice", instructions: MATCH_INSTRUCTIONS, criteria: { none: "None", match: name } },
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
51
|
function matchProbability(result, id) {
|
|
60
52
|
const answer = result.answers[id];
|
|
61
53
|
const probabilities = answer?.probabilities;
|
|
@@ -105,9 +97,89 @@ export async function decidePiCodemode(input) {
|
|
|
105
97
|
return { override: "off", source: "local" };
|
|
106
98
|
return { source: "uncertain" };
|
|
107
99
|
}
|
|
108
|
-
|
|
100
|
+
const SKILL_GROUP_SIZE = 4;
|
|
101
|
+
const SKILL_GROUPS_PER_REQUEST = 4;
|
|
102
|
+
const SKILL_DESCRIPTION_CHARS = 48;
|
|
103
|
+
const NO_SKILL = "none";
|
|
104
|
+
function compactSkillDescription(item) {
|
|
105
|
+
const description = item.description.replace(/\s+/g, " ").trim();
|
|
106
|
+
return `${item.name}: ${Array.from(description).slice(0, SKILL_DESCRIPTION_CHARS).join("")}`;
|
|
107
|
+
}
|
|
108
|
+
function skillDecisionGroup(items, index) {
|
|
109
|
+
return { candidates: items.map((item, option) => ({ item, option: `s${option}` })),
|
|
110
|
+
forwardId: `g${index}f`, reverseId: `g${index}r` };
|
|
111
|
+
}
|
|
112
|
+
function skillDecisionCriteria(group, reverse) {
|
|
113
|
+
const entries = group.candidates.map((candidate) => [candidate.option, compactSkillDescription(candidate.item)]);
|
|
114
|
+
entries.push([NO_SKILL, "No installed skill is relevant"]);
|
|
115
|
+
if (reverse)
|
|
116
|
+
entries.reverse();
|
|
117
|
+
return Object.fromEntries(entries);
|
|
118
|
+
}
|
|
119
|
+
function skillDecisionQuestions(groups) {
|
|
120
|
+
const questions = {};
|
|
121
|
+
for (const group of groups) {
|
|
122
|
+
questions[group.forwardId] = { type: "choice", instructions: "Which installed skill best fits the task?",
|
|
123
|
+
criteria: skillDecisionCriteria(group, false) };
|
|
124
|
+
questions[group.reverseId] = { type: "choice", instructions: "Which installed skill best fits the task?",
|
|
125
|
+
criteria: skillDecisionCriteria(group, true) };
|
|
126
|
+
}
|
|
127
|
+
return questions;
|
|
128
|
+
}
|
|
129
|
+
function readSkillChoice(result, id, labels) {
|
|
130
|
+
const answer = result.answers[id];
|
|
131
|
+
const probabilities = answer?.probabilities;
|
|
132
|
+
const values = labels.map((label) => probabilities?.[label]);
|
|
133
|
+
if (answer?.type !== "choice" || typeof answer.choice !== "string" || !labels.includes(answer.choice)
|
|
134
|
+
|| !probabilities || Object.keys(probabilities).length !== labels.length
|
|
135
|
+
|| values.some((value) => typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1)
|
|
136
|
+
|| Math.abs(values.reduce((sum, value) => sum + value, 0) - 1) > 0.002
|
|
137
|
+
|| probabilities[answer.choice] < Math.max(...values)) {
|
|
138
|
+
throw new DecisionError("INVALID_RESULT", "决策员工返回无效的 Skills 候选判断,原选择未改变。", 502);
|
|
139
|
+
}
|
|
140
|
+
return { choice: answer.choice, probabilities: probabilities };
|
|
141
|
+
}
|
|
142
|
+
function readSkillPair(result, group) {
|
|
143
|
+
const labels = [...group.candidates.map((candidate) => candidate.option), NO_SKILL];
|
|
144
|
+
return { group, forward: readSkillChoice(result, group.forwardId, labels),
|
|
145
|
+
reverse: readSkillChoice(result, group.reverseId, labels) };
|
|
146
|
+
}
|
|
147
|
+
function averageSkillScore(pair, option) {
|
|
148
|
+
return (pair.forward.probabilities[option] + pair.reverse.probabilities[option]) / 2;
|
|
149
|
+
}
|
|
150
|
+
function strongestSkill(pair) {
|
|
151
|
+
let winner = pair.group.candidates[0];
|
|
152
|
+
let score = -1;
|
|
153
|
+
for (const candidate of pair.group.candidates) {
|
|
154
|
+
const next = averageSkillScore(pair, candidate.option);
|
|
155
|
+
if (next > score) {
|
|
156
|
+
winner = candidate;
|
|
157
|
+
score = next;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return winner.item;
|
|
161
|
+
}
|
|
162
|
+
/** Average the two reversed option orders; an exact aggregate tie is not actionable. */
|
|
163
|
+
function strongestSkillOption(pair) {
|
|
164
|
+
let winner = null;
|
|
165
|
+
let score = -1;
|
|
166
|
+
let tied = false;
|
|
167
|
+
for (const option of [...pair.group.candidates.map((candidate) => candidate.option), NO_SKILL]) {
|
|
168
|
+
const next = averageSkillScore(pair, option);
|
|
169
|
+
if (next > score + Number.EPSILON) {
|
|
170
|
+
winner = option;
|
|
171
|
+
score = next;
|
|
172
|
+
tied = false;
|
|
173
|
+
}
|
|
174
|
+
else if (Math.abs(next - score) <= Number.EPSILON)
|
|
175
|
+
tied = true;
|
|
176
|
+
}
|
|
177
|
+
return tied ? null : winner;
|
|
178
|
+
}
|
|
179
|
+
/** Read-only matching. Bounded system-role decisions rank the candidate set; they never connect MCP or grant authority. */
|
|
109
180
|
export async function recommendPiResources(input) {
|
|
110
|
-
const
|
|
181
|
+
const statuses = new Map();
|
|
182
|
+
const semanticSkills = [];
|
|
111
183
|
const task = resourceTask(input.prompt, input.catalog);
|
|
112
184
|
let calls = 0;
|
|
113
185
|
for (const kind of ["skills", "mcpServers"]) {
|
|
@@ -119,48 +191,63 @@ export async function recommendPiResources(input) {
|
|
|
119
191
|
throw new DecisionError("RESOURCE_MISSING", "候选资源已移除,请重新打开设置面板。");
|
|
120
192
|
const mention = explicitMention(input.prompt, item.name);
|
|
121
193
|
if (mention) {
|
|
122
|
-
|
|
123
|
-
continue;
|
|
194
|
+
statuses.set(id, { id, kind, status: mention === "included" ? "recommended" : "unmatched", source: "explicit" });
|
|
124
195
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
continue;
|
|
196
|
+
else if (!task || kind === "mcpServers" || item.description.length > 1000) {
|
|
197
|
+
// Transport type is not a capability description. Never connect MCP for discovery.
|
|
198
|
+
statuses.set(id, { id, kind, status: "unassessed", source: "local" });
|
|
129
199
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
200
|
+
else {
|
|
201
|
+
semanticSkills.push(item);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (semanticSkills.length) {
|
|
206
|
+
try {
|
|
207
|
+
const groups = Array.from({ length: Math.ceil(semanticSkills.length / SKILL_GROUP_SIZE) }, (_, index) => skillDecisionGroup(semanticSkills.slice(index * SKILL_GROUP_SIZE, (index + 1) * SKILL_GROUP_SIZE), index));
|
|
208
|
+
const pairs = [];
|
|
209
|
+
for (let index = 0; index < groups.length; index += SKILL_GROUPS_PER_REQUEST) {
|
|
140
210
|
if (input.signal?.aborted)
|
|
141
211
|
throw new DecisionError("CANCELLED", "推荐已取消。", 499);
|
|
142
|
-
|
|
212
|
+
const batch = groups.slice(index, index + SKILL_GROUPS_PER_REQUEST);
|
|
143
213
|
calls++;
|
|
144
|
-
result = await input.evaluate({
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
214
|
+
const result = await input.evaluate({ state: task, questions: skillDecisionQuestions(batch) }, input.caller, input.signal);
|
|
215
|
+
if (input.signal?.aborted)
|
|
216
|
+
throw new DecisionError("CANCELLED", "推荐已取消。", 499);
|
|
217
|
+
pairs.push(...batch.map((group) => readSkillPair(result, group)));
|
|
148
218
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
throw error;
|
|
219
|
+
let finalPair;
|
|
220
|
+
if (pairs.length === 1) {
|
|
221
|
+
finalPair = pairs[0];
|
|
155
222
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
223
|
+
else {
|
|
224
|
+
const finalists = pairs.map(strongestSkill);
|
|
225
|
+
const finalGroup = skillDecisionGroup(finalists, groups.length);
|
|
226
|
+
calls++;
|
|
227
|
+
const result = await input.evaluate({ state: task, questions: skillDecisionQuestions([finalGroup]) }, input.caller, input.signal);
|
|
228
|
+
if (input.signal?.aborted)
|
|
229
|
+
throw new DecisionError("CANCELLED", "推荐已取消。", 499);
|
|
230
|
+
finalPair = readSkillPair(result, finalGroup);
|
|
159
231
|
}
|
|
160
|
-
const
|
|
161
|
-
|
|
232
|
+
const winner = strongestSkillOption(finalPair);
|
|
233
|
+
const selected = winner && winner !== NO_SKILL
|
|
234
|
+
? finalPair.group.candidates.find((candidate) => candidate.option === winner)?.item.id : undefined;
|
|
235
|
+
for (const item of semanticSkills)
|
|
236
|
+
statuses.set(item.id, { id: item.id, kind: "skills",
|
|
237
|
+
status: item.id === selected ? "recommended" : winner === NO_SKILL ? "unmatched" : "uncertain", source: "local" });
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
if (!(error instanceof DecisionError) || !["CONTEXT_LIMIT", "QUESTION_LIMIT", "OPTIONS_COLLAPSED"].includes(error.code))
|
|
241
|
+
throw error;
|
|
242
|
+
for (const item of semanticSkills)
|
|
243
|
+
statuses.set(item.id, { id: item.id, kind: "skills", status: "unassessed", source: "local" });
|
|
162
244
|
}
|
|
163
245
|
}
|
|
246
|
+
const resources = [];
|
|
247
|
+
for (const kind of ["skills", "mcpServers"]) {
|
|
248
|
+
for (const id of input.candidates[kind])
|
|
249
|
+
resources.push(statuses.get(id));
|
|
250
|
+
}
|
|
164
251
|
const selection = { skills: [], mcpServers: [] };
|
|
165
252
|
for (const resource of resources)
|
|
166
253
|
if (resource.status === "recommended")
|
|
@@ -4,6 +4,7 @@ import process from "node:process";
|
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import { DatabaseSync } from "node:sqlite";
|
|
6
6
|
import { getProviderResumeCommandSessionId } from "./resume-policy.js";
|
|
7
|
+
import { parseTimestampMs as parseTimeMs } from "./time-utils.js";
|
|
7
8
|
const REAL_CONVERSATION_MIN_LINES = 2;
|
|
8
9
|
const DISCOVERY_RECENT_WINDOW_MS = 10 * 60 * 1000;
|
|
9
10
|
const START_TIME_SKEW_MS = 30 * 1000;
|
|
@@ -173,12 +174,6 @@ function isSameResolvedPath(left, right) {
|
|
|
173
174
|
function isUsableCodexHistorySession(session) {
|
|
174
175
|
return session.hasUser || session.hasConversation;
|
|
175
176
|
}
|
|
176
|
-
function parseTimeMs(value) {
|
|
177
|
-
if (!value)
|
|
178
|
-
return null;
|
|
179
|
-
const parsed = Date.parse(value);
|
|
180
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
181
|
-
}
|
|
182
177
|
function selectCodexSessionForRecord(record, sessions) {
|
|
183
178
|
const knownMtimes = record.knownCodexSessionMtimes ?? new Map();
|
|
184
179
|
const candidates = sessions
|
|
@@ -243,7 +243,6 @@ export declare class ProcessManager extends EventEmitter {
|
|
|
243
243
|
deleteQoderHistoryFiles(sessionIds: string[]): number;
|
|
244
244
|
deletePiHistoryFiles(sessionIds: string[]): number;
|
|
245
245
|
deleteGrokHistoryFiles(sessionIds: string[]): number;
|
|
246
|
-
deleteGeminiHistoryFiles(sessionIds: string[]): number;
|
|
247
246
|
private captureCodexSessionId;
|
|
248
247
|
private captureOpenCodeSessionId;
|
|
249
248
|
private captureClaudeSessionId;
|
package/dist/process-manager.js
CHANGED
|
@@ -674,6 +674,9 @@ export class ProcessManager extends EventEmitter {
|
|
|
674
674
|
async start(command, cwd, mode, initialInput, opts) {
|
|
675
675
|
if (this.disposed)
|
|
676
676
|
throw new Error("ProcessManager has been disposed.");
|
|
677
|
+
if (opts?.provider != null && !isSessionProvider(opts.provider)) {
|
|
678
|
+
throw new Error(`不支持的终端 provider:${String(opts.provider)}`);
|
|
679
|
+
}
|
|
677
680
|
const id = opts?.reuseId || randomUUID();
|
|
678
681
|
// Host operations are keyed by session id, not by the manager record. A
|
|
679
682
|
// second handshake must not adopt/forget a cancelled first incarnation.
|
|
@@ -798,10 +801,10 @@ export class ProcessManager extends EventEmitter {
|
|
|
798
801
|
? getProviderCommandSessionId(provider, processedCommand)
|
|
799
802
|
?? getProviderCommandSessionId(provider, command)
|
|
800
803
|
: null;
|
|
801
|
-
// Grok / Qoder
|
|
804
|
+
// Grok / Qoder accept caller-selected IDs for new conversations. Assigning
|
|
802
805
|
// one up front avoids depending on provider-specific TUI rendering to learn
|
|
803
806
|
// the durable ID later.
|
|
804
|
-
const assignedProviderSessionId = !existingProviderSessionId && (provider === "grok" || provider === "qoder"
|
|
807
|
+
const assignedProviderSessionId = !existingProviderSessionId && (provider === "grok" || provider === "qoder")
|
|
805
808
|
? randomUUID()
|
|
806
809
|
: null;
|
|
807
810
|
if (assignedProviderSessionId) {
|
|
@@ -1129,9 +1132,6 @@ export class ProcessManager extends EventEmitter {
|
|
|
1129
1132
|
deleteGrokHistoryFiles(sessionIds) {
|
|
1130
1133
|
return this.providerHistory.deleteGrokHistoryFiles(sessionIds);
|
|
1131
1134
|
}
|
|
1132
|
-
deleteGeminiHistoryFiles(sessionIds) {
|
|
1133
|
-
return this.providerHistory.deleteGeminiHistoryFiles(sessionIds);
|
|
1134
|
-
}
|
|
1135
1135
|
captureCodexSessionId(record, options) {
|
|
1136
1136
|
if (record.provider !== "codex" || record.claudeSessionId) {
|
|
1137
1137
|
return false;
|
|
@@ -2263,19 +2263,6 @@ export class ProcessManager extends EventEmitter {
|
|
|
2263
2263
|
result += ` --thinking '${level}'`;
|
|
2264
2264
|
return result;
|
|
2265
2265
|
}
|
|
2266
|
-
if (provider === "gemini") {
|
|
2267
|
-
let result = command;
|
|
2268
|
-
const trimmedModel = model?.trim();
|
|
2269
|
-
if (trimmedModel && trimmedModel !== "default" && !/--model(?:\s|=)/.test(result) && !/(?:^|\s)-m(?:\s|$)/.test(result)) {
|
|
2270
|
-
result += ` --model '${trimmedModel.replace(/'/g, "'\\''")}'`;
|
|
2271
|
-
}
|
|
2272
|
-
// Gemini CLI 没有 thinking 档位开关。approval mode 与其它 provider 的
|
|
2273
|
-
// auto-approve 语义对齐,default 下让 TUI 自己弹确认。
|
|
2274
|
-
if ((mode === "managed" || mode === "full-access" || mode === "auto-edit") && !/--approval-mode(?:\s|=)/.test(result) && !/(?:^|\s)-y(?:\s|$)/.test(result)) {
|
|
2275
|
-
result += " --approval-mode yolo";
|
|
2276
|
-
}
|
|
2277
|
-
return result;
|
|
2278
|
-
}
|
|
2279
2266
|
const isClaudeCmd = /^(?:claude|npx\s+claude|[^\s]+\/claude)(?:\s|$)/.test(command);
|
|
2280
2267
|
if (!isClaudeCmd)
|
|
2281
2268
|
return command;
|
|
@@ -6,11 +6,10 @@
|
|
|
6
6
|
* 都直接复用它,避免同一个 provider 概念散落在两处各写一份。
|
|
7
7
|
*/
|
|
8
8
|
/** 全部受支持的会话 provider;顺序即 UI / 派发选择器里的展示顺序。 */
|
|
9
|
-
export declare const SESSION_PROVIDERS: readonly ["claude", "codex", "opencode", "grok", "qoder", "pi"
|
|
9
|
+
export declare const SESSION_PROVIDERS: readonly ["claude", "codex", "opencode", "grok", "qoder", "pi"];
|
|
10
10
|
export type SessionProvider = (typeof SESSION_PROVIDERS)[number];
|
|
11
11
|
/**
|
|
12
12
|
* 拥有原生思考档位(`provider:level`)的 provider。
|
|
13
|
-
* Gemini CLI 没有档位开关,所以不出现在这里:`gemini:max` 这种值不应该被当成合法档位保留。
|
|
14
13
|
*/
|
|
15
14
|
export declare const NATIVE_THINKING_EFFORT_PROVIDERS: readonly ["claude", "codex", "opencode", "grok", "qoder", "pi"];
|
|
16
15
|
/** 原生思考档位的 `provider:level` 形状;各端必须共用同一张 provider 名单。 */
|
package/dist/provider-catalog.js
CHANGED
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
* 都直接复用它,避免同一个 provider 概念散落在两处各写一份。
|
|
7
7
|
*/
|
|
8
8
|
/** 全部受支持的会话 provider;顺序即 UI / 派发选择器里的展示顺序。 */
|
|
9
|
-
export const SESSION_PROVIDERS = ["claude", "codex", "opencode", "grok", "qoder", "pi"
|
|
9
|
+
export const SESSION_PROVIDERS = ["claude", "codex", "opencode", "grok", "qoder", "pi"];
|
|
10
10
|
/**
|
|
11
11
|
* 拥有原生思考档位(`provider:level`)的 provider。
|
|
12
|
-
* Gemini CLI 没有档位开关,所以不出现在这里:`gemini:max` 这种值不应该被当成合法档位保留。
|
|
13
12
|
*/
|
|
14
13
|
export const NATIVE_THINKING_EFFORT_PROVIDERS = ["claude", "codex", "opencode", "grok", "qoder", "pi"];
|
|
15
14
|
/** 原生思考档位的 `provider:level` 形状;各端必须共用同一张 provider 名单。 */
|
|
@@ -41,7 +40,6 @@ export const PROVIDER_LABELS = {
|
|
|
41
40
|
grok: "Grok",
|
|
42
41
|
qoder: "Qoder",
|
|
43
42
|
pi: "Pi",
|
|
44
|
-
gemini: "Gemini",
|
|
45
43
|
};
|
|
46
44
|
/**
|
|
47
45
|
* Wand 自带 Agent(进程内 `core` harness / SDK)的展示名。
|
|
@@ -79,9 +77,6 @@ export const PROVIDER_ALIASES = {
|
|
|
79
77
|
pi: "pi",
|
|
80
78
|
"pi-cli": "pi",
|
|
81
79
|
"pi-cli-json": "pi",
|
|
82
|
-
gemini: "gemini",
|
|
83
|
-
"gemini-cli": "gemini",
|
|
84
|
-
"gemini-cli-json": "gemini",
|
|
85
80
|
};
|
|
86
81
|
/** 归一化一个已持久化的 provider id 或 runner/别名;无法识别时返回 null。 */
|
|
87
82
|
export function normalizeProviderId(value) {
|
|
@@ -63,14 +63,6 @@ const PROVIDER_CLI_SPECS = [
|
|
|
63
63
|
versionArgs: ["--version"],
|
|
64
64
|
updateArgs: ["update", "self"],
|
|
65
65
|
},
|
|
66
|
-
{
|
|
67
|
-
id: "gemini",
|
|
68
|
-
label: "Gemini CLI",
|
|
69
|
-
command: "gemini",
|
|
70
|
-
npmPackage: "@google/gemini-cli",
|
|
71
|
-
versionArgs: ["--version"],
|
|
72
|
-
// 没有 `gemini update` 子命令,只能走 npm 发布渠道。
|
|
73
|
-
},
|
|
74
66
|
];
|
|
75
67
|
async function runCommand(command, args, timeout, options) {
|
|
76
68
|
const { stdout, stderr } = await execFileAsync(command, args, {
|
|
@@ -351,7 +343,7 @@ export async function updateProviderClis(statuses, ids, options = {}) {
|
|
|
351
343
|
output = await runCommand(executable, spec.updateArgs, options.updateTimeoutMs ?? UPDATE_TIMEOUT_MS, options);
|
|
352
344
|
}
|
|
353
345
|
else {
|
|
354
|
-
// 没有自更新子命令的 CLI
|
|
346
|
+
// 没有自更新子命令的 CLI 走它的 npm 发布渠道。
|
|
355
347
|
output = await runCommand(npmBin(options), ["install", "-g", `${spec.npmPackage}@latest`], options.updateTimeoutMs ?? UPDATE_TIMEOUT_MS, options);
|
|
356
348
|
}
|
|
357
349
|
if (spec.id === "codex") {
|
|
@@ -50,7 +50,6 @@ export interface ProviderHistoryScannerOptions {
|
|
|
50
50
|
qoderProjectsDirs?: string[];
|
|
51
51
|
piSessionsDir?: string;
|
|
52
52
|
grokSessionsDir?: string;
|
|
53
|
-
geminiHome?: string;
|
|
54
53
|
}
|
|
55
54
|
/**
|
|
56
55
|
* Incremental provider-history index. Directory entries are refreshed on each
|
|
@@ -64,7 +63,6 @@ export declare class ProviderHistoryScanner {
|
|
|
64
63
|
private readonly qoderProjectsDirs;
|
|
65
64
|
private readonly piSessionsDir;
|
|
66
65
|
private readonly grokSessionsDir;
|
|
67
|
-
private readonly geminiHome;
|
|
68
66
|
private readonly claudeIndex;
|
|
69
67
|
private readonly codexIndex;
|
|
70
68
|
private readonly qoderIndex;
|
|
@@ -96,5 +94,4 @@ export declare class ProviderHistoryScanner {
|
|
|
96
94
|
deleteQoderHistoryFiles(sessionIds: string[]): number;
|
|
97
95
|
deletePiHistoryFiles(sessionIds: string[]): number;
|
|
98
96
|
deleteGrokHistoryFiles(sessionIds: string[]): number;
|
|
99
|
-
deleteGeminiHistoryFiles(sessionIds: string[]): number;
|
|
100
97
|
}
|
|
@@ -266,7 +266,6 @@ export class ProviderHistoryScanner {
|
|
|
266
266
|
qoderProjectsDirs;
|
|
267
267
|
piSessionsDir;
|
|
268
268
|
grokSessionsDir;
|
|
269
|
-
geminiHome;
|
|
270
269
|
claudeIndex = new Map();
|
|
271
270
|
codexIndex = new Map();
|
|
272
271
|
qoderIndex = new Map();
|
|
@@ -285,7 +284,6 @@ export class ProviderHistoryScanner {
|
|
|
285
284
|
];
|
|
286
285
|
this.piSessionsDir = options.piSessionsDir ?? path.join(os.homedir(), ".pi", "agent", "sessions");
|
|
287
286
|
this.grokSessionsDir = options.grokSessionsDir ?? path.join(os.homedir(), ".grok", "sessions");
|
|
288
|
-
this.geminiHome = options.geminiHome ?? path.join(os.homedir(), ".gemini");
|
|
289
287
|
}
|
|
290
288
|
getDiagnostics() {
|
|
291
289
|
return {
|
|
@@ -729,28 +727,4 @@ export class ProviderHistoryScanner {
|
|
|
729
727
|
}
|
|
730
728
|
return deleted;
|
|
731
729
|
}
|
|
732
|
-
deleteGeminiHistoryFiles(sessionIds) {
|
|
733
|
-
const ids = new Set(sessionIds.filter((id) => PROVIDER_SESSION_ID_PATTERN.test(id)));
|
|
734
|
-
if (ids.size === 0 || !existsSync(this.geminiHome))
|
|
735
|
-
return 0;
|
|
736
|
-
let deleted = 0;
|
|
737
|
-
for (const id of ids) {
|
|
738
|
-
const dbPath = path.join(this.geminiHome, "antigravity-cli", "conversations", `${id}.db`);
|
|
739
|
-
if (existsSync(dbPath)) {
|
|
740
|
-
try {
|
|
741
|
-
unlinkSync(dbPath);
|
|
742
|
-
deleted += 1;
|
|
743
|
-
}
|
|
744
|
-
catch { /* already absent */ }
|
|
745
|
-
}
|
|
746
|
-
const lockPath = path.join(this.geminiHome, "antigravity-cli", "presence", `${id}.lock`);
|
|
747
|
-
if (existsSync(lockPath)) {
|
|
748
|
-
try {
|
|
749
|
-
unlinkSync(lockPath);
|
|
750
|
-
}
|
|
751
|
-
catch { /* best effort */ }
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
return deleted;
|
|
755
|
-
}
|
|
756
730
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { lstatSync, readFileSync } from "node:fs";
|
|
2
2
|
import net from "node:net";
|
|
3
3
|
import process from "node:process";
|
|
4
|
-
import { rejectPendingRequests, startDaemonHeartbeat, waitForDaemonSocket } from "./daemon-connection.js";
|
|
4
|
+
import { emitToSubscribedListeners, rejectPendingRequests, startDaemonHeartbeat, subscribeWithBufferedEvents, subscribeWithPendingEvent, waitForDaemonSocket } from "./daemon-connection.js";
|
|
5
5
|
import { getErrorMessage } from "./error-utils.js";
|
|
6
6
|
import { appendWindow, PTY_OUTPUT_MAX_SIZE } from "./pty-text-utils.js";
|
|
7
7
|
import { MAX_FRAME_BYTES, RENDER_PROTOCOL_VERSION, encodeRenderFrame, renderPaths, } from "./render-protocol.js";
|
|
@@ -71,30 +71,13 @@ class RemoteRenderProcess {
|
|
|
71
71
|
this.client.fireOperation(this.sessionId, "kill", { sessionId: this.sessionId, signal });
|
|
72
72
|
}
|
|
73
73
|
onData(listener) {
|
|
74
|
-
this.
|
|
75
|
-
if (
|
|
76
|
-
const pending = this.undeliveredEvents;
|
|
74
|
+
const pending = this.undeliveredEvents;
|
|
75
|
+
if (pending.length > 0)
|
|
77
76
|
this.undeliveredEvents = [];
|
|
78
|
-
|
|
79
|
-
if (!this.dataListeners.has(listener))
|
|
80
|
-
return;
|
|
81
|
-
for (const event of pending)
|
|
82
|
-
listener(event);
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
return { dispose: () => this.dataListeners.delete(listener) };
|
|
77
|
+
return subscribeWithBufferedEvents(this.dataListeners, listener, pending);
|
|
86
78
|
}
|
|
87
79
|
onExit(listener) {
|
|
88
|
-
this.exitListeners.
|
|
89
|
-
const pending = this.pendingExit;
|
|
90
|
-
if (pending) {
|
|
91
|
-
// 退出事件只发生一次,但可能早于 listener 到达;补发语义必须与 data 一致。
|
|
92
|
-
queueMicrotask(() => {
|
|
93
|
-
if (this.exitListeners.has(listener))
|
|
94
|
-
listener(pending);
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
return { dispose: () => this.exitListeners.delete(listener) };
|
|
80
|
+
return subscribeWithPendingEvent(this.exitListeners, listener, this.pendingExit);
|
|
98
81
|
}
|
|
99
82
|
onResync(listener) {
|
|
100
83
|
this.resyncListeners.add(listener);
|
|
@@ -122,21 +105,15 @@ class RemoteRenderProcess {
|
|
|
122
105
|
listener(state);
|
|
123
106
|
}
|
|
124
107
|
acceptData(event) {
|
|
125
|
-
if (this.dataListeners
|
|
108
|
+
if (!emitToSubscribedListeners(this.dataListeners, event)) {
|
|
126
109
|
this.undeliveredEvents = appendTerminalChunkWindow(this.undeliveredEvents, event);
|
|
127
|
-
return;
|
|
128
110
|
}
|
|
129
|
-
this.emitData(event);
|
|
130
111
|
}
|
|
131
112
|
acceptExit(event) {
|
|
132
113
|
this.pendingExit = event;
|
|
133
114
|
for (const listener of Array.from(this.exitListeners))
|
|
134
115
|
listener(event);
|
|
135
116
|
}
|
|
136
|
-
emitData(event) {
|
|
137
|
-
for (const listener of Array.from(this.dataListeners))
|
|
138
|
-
listener(event);
|
|
139
|
-
}
|
|
140
117
|
}
|
|
141
118
|
/**
|
|
142
119
|
* Render(常驻 PTY 持有者)的 Node 客户端。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { lstatSync, readFileSync } from "node:fs";
|
|
2
2
|
import net from "node:net";
|
|
3
|
-
import { rejectPendingRequests, startDaemonHeartbeat, waitForDaemonSocket } from "./daemon-connection.js";
|
|
3
|
+
import { rejectPendingRequests, startDaemonHeartbeat, subscribeWithPendingEvent, waitForDaemonSocket } from "./daemon-connection.js";
|
|
4
4
|
import { getErrorMessage } from "./error-utils.js";
|
|
5
5
|
import { STRUCTURED_RENDER_PROTOCOL_VERSION, decodeRenderFrames, encodeRenderFrame, structuredRenderPaths, } from "./render-structured-protocol.js";
|
|
6
6
|
import { logInfo } from "./runtime-log.js";
|
|
@@ -51,12 +51,7 @@ class RemoteRun {
|
|
|
51
51
|
return { dispose: () => this.streamListeners.delete(listener) };
|
|
52
52
|
}
|
|
53
53
|
onExit(listener) {
|
|
54
|
-
this.exitListeners.
|
|
55
|
-
const event = this.pendingExit;
|
|
56
|
-
if (event)
|
|
57
|
-
queueMicrotask(() => { if (this.exitListeners.has(listener))
|
|
58
|
-
listener(event); });
|
|
59
|
-
return { dispose: () => this.exitListeners.delete(listener) };
|
|
54
|
+
return subscribeWithPendingEvent(this.exitListeners, listener, this.pendingExit);
|
|
60
55
|
}
|
|
61
56
|
acceptStream(event) {
|
|
62
57
|
const last = this.seq[event.stream];
|
package/dist/retention.js
CHANGED
|
@@ -2,6 +2,7 @@ import { AI_TEAM_ACTIVE_RUN_STATUSES } from "./ai-team-types.js";
|
|
|
2
2
|
import { isArchiveBlocked } from "./archive-policy.js";
|
|
3
3
|
import { cleanupWorktreeSync } from "./git-worktree.js";
|
|
4
4
|
import { defaultTaskRetention, taskRetentionWindows } from "./task-retention.js";
|
|
5
|
+
import { parseTimestampMs as timestamp } from "./time-utils.js";
|
|
5
6
|
/** Backward-compatible exports for consumers of the historical default windows. */
|
|
6
7
|
const DEFAULT_RETENTION_WINDOWS = taskRetentionWindows(defaultTaskRetention());
|
|
7
8
|
export const RETENTION_IDLE_MS = DEFAULT_RETENTION_WINDOWS.idleMs;
|
|
@@ -17,12 +18,6 @@ function isIdleLivePty(session) {
|
|
|
17
18
|
return session.status === "running" && !isRetentionBusy(session)
|
|
18
19
|
&& ((session.sessionKind ?? "pty") === "pty" || session.runner === "pty");
|
|
19
20
|
}
|
|
20
|
-
function timestamp(value) {
|
|
21
|
-
if (!value)
|
|
22
|
-
return null;
|
|
23
|
-
const parsed = Date.parse(value);
|
|
24
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
25
|
-
}
|
|
26
21
|
/**
|
|
27
22
|
* 任务的「最后一次活动」:卡片自身的 updated_at、容器最后一次被打开的时间,
|
|
28
23
|
* 以及它名下会话的结束/开始时间,取三者里最新的一个。
|
package/dist/run-command.d.ts
CHANGED
|
@@ -16,6 +16,11 @@ export interface RunCommandOptions {
|
|
|
16
16
|
stdio?: SpawnSyncOptionsWithStringEncoding["stdio"];
|
|
17
17
|
}
|
|
18
18
|
export declare function runCommandSync(file: string, args: readonly string[], options?: RunCommandOptions): SpawnSyncReturns<string>;
|
|
19
|
+
/** Foreground operations such as sudo: the terminal owns input (including passwords).
|
|
20
|
+
* No capture, shell, or timeout: cancellation belongs to the user, and Core drain
|
|
21
|
+
* may legitimately wait longer than the bounded probe timeout.
|
|
22
|
+
*/
|
|
23
|
+
export declare function runCommandInteractive(file: string, args: readonly string[]): Promise<number>;
|
|
19
24
|
export interface RunCommandResult {
|
|
20
25
|
ok: boolean;
|
|
21
26
|
code: number | null;
|