@co0ontty/wand 4.76.0 → 4.77.0-beta.g6c4df97
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/dist/agent-dispatch.d.ts +46 -0
- package/dist/agent-dispatch.js +84 -0
- package/dist/ai-team-availability.d.ts +69 -0
- package/dist/ai-team-availability.js +100 -0
- package/dist/ai-team-prompts.d.ts +113 -0
- package/dist/ai-team-prompts.js +435 -0
- package/dist/ai-team-runner.d.ts +241 -0
- package/dist/ai-team-runner.js +1370 -0
- package/dist/ai-team-types.d.ts +147 -0
- package/dist/ai-team-types.js +32 -0
- package/dist/attention.d.ts +40 -0
- package/dist/attention.js +86 -0
- package/dist/build-info.json +4 -4
- package/dist/claude-sdk-runner.d.ts +5 -0
- package/dist/claude-sdk-runner.js +2 -1
- package/dist/commit-task-archive.d.ts +3 -0
- package/dist/commit-task-archive.js +43 -0
- package/dist/config.js +0 -2
- package/dist/git-quick-commit.d.ts +2 -1
- package/dist/git-quick-commit.js +72 -60
- package/dist/message-truncator.d.ts +41 -5
- package/dist/message-truncator.js +255 -38
- package/dist/missions.d.ts +4 -2
- package/dist/missions.js +2 -2
- package/dist/process-manager.d.ts +1 -0
- package/dist/process-manager.js +17 -8
- package/dist/prompt-optimizer.js +13 -13
- package/dist/server-ai-team-routes.d.ts +11 -0
- package/dist/server-ai-team-routes.js +283 -0
- package/dist/server-attention-routes.d.ts +9 -0
- package/dist/server-attention-routes.js +26 -0
- package/dist/server-file-routes.js +1 -1
- package/dist/server-session-routes.js +41 -5
- package/dist/server-settings-routes.js +1 -1
- package/dist/server-task-routes.d.ts +2 -0
- package/dist/server-task-routes.js +6 -40
- package/dist/server-workspace-routes.js +13 -5
- package/dist/server.js +43 -0
- package/dist/session-topic.js +13 -13
- package/dist/session-transport.d.ts +3 -0
- package/dist/session-transport.js +1 -0
- package/dist/storage.d.ts +54 -1
- package/dist/storage.js +413 -2
- package/dist/structured-claude-adapter.d.ts +5 -0
- package/dist/structured-claude-adapter.js +13 -2
- package/dist/structured-claude-protocol.d.ts +2 -0
- package/dist/structured-claude-protocol.js +4 -0
- package/dist/structured-codex-adapter.js +2 -2
- package/dist/structured-grok-adapter.js +2 -1
- package/dist/structured-opencode-adapter.js +2 -2
- package/dist/structured-pi-adapter.js +2 -1
- package/dist/structured-provider-common.d.ts +17 -0
- package/dist/structured-provider-common.js +52 -0
- package/dist/structured-qoder-adapter.js +2 -1
- package/dist/structured-session-manager.d.ts +18 -1
- package/dist/structured-session-manager.js +67 -10
- package/dist/system-ai.d.ts +13 -2
- package/dist/system-ai.js +9 -5
- package/dist/task-title.js +10 -10
- package/dist/types.d.ts +25 -2
- package/dist/wand-task-sync.d.ts +1 -0
- package/dist/wand-task-sync.js +1 -0
- package/dist/web-ui/content/ai-teams.js +12 -0
- package/dist/web-ui/content/scripts.js +86 -5301
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.d.ts +1 -0
- package/dist/web-ui/embedded-assets.js +3 -2
- package/dist/web-ui/scripts.d.ts +8 -0
- package/dist/web-ui/scripts.js +28 -2
- package/dist/ws-broadcast.js +52 -19
- package/package.json +1 -1
package/dist/storage.js
CHANGED
|
@@ -4,9 +4,10 @@ import path from "node:path";
|
|
|
4
4
|
import { DatabaseSync } from "node:sqlite";
|
|
5
5
|
import { GLOBAL_WORKSPACE_ID } from "./types.js";
|
|
6
6
|
import { normalizeSessionDirectory } from "./session-directory-tree.js";
|
|
7
|
-
import { inferProviderFromCommand, inferProviderFromRunner, isSessionProvider } from "./session-provider.js";
|
|
7
|
+
import { inferProviderFromCommand, inferProviderFromRunner, isSessionProvider, SESSION_PROVIDERS } from "./session-provider.js";
|
|
8
8
|
import { DEFAULT_ITERATION_NAME, DEFAULT_WAND_TASK_AGENT_KIND, DEFAULT_WAND_TASK_PRIORITY, isWandTaskAgentKind, normalizeWandTaskAgentMode } from "./task-types.js";
|
|
9
9
|
import { firstLayoutTabId } from "./layout-tree.js";
|
|
10
|
+
import { AI_TEAM_DEFAULT_MAX_STEPS, isTeamMemberRole, memberAgents } from "./ai-team-types.js";
|
|
10
11
|
import { isThinkingEffort } from "./structured-provider-common.js";
|
|
11
12
|
import { DEFAULT_PASSWORD_VAULT_ID, DEFAULT_PASSWORD_VAULT_NAME, decryptVaultSecret, encryptVaultSecret, itemMatchesFilter, normalizePasswordItemInput, normalizeVaultName, nowIso, } from "./password-manager.js";
|
|
12
13
|
function safeJsonParse(raw) {
|
|
@@ -24,7 +25,11 @@ function safeJsonParse(raw) {
|
|
|
24
25
|
* 前端据此显示「未指定 CLI 工具」,而不是渲染出半个派发配置。
|
|
25
26
|
*/
|
|
26
27
|
export function parseWandTaskAgent(raw) {
|
|
27
|
-
const value = typeof raw === "string"
|
|
28
|
+
const value = typeof raw === "string"
|
|
29
|
+
? safeJsonParse(raw)
|
|
30
|
+
: raw && typeof raw === "object" && !Array.isArray(raw)
|
|
31
|
+
? raw
|
|
32
|
+
: undefined;
|
|
28
33
|
if (!value || typeof value !== "object")
|
|
29
34
|
return null;
|
|
30
35
|
const provider = value.provider;
|
|
@@ -170,6 +175,7 @@ function serializeSessionOptions(snapshot) {
|
|
|
170
175
|
providerCliExitCode: snapshot.providerCliExitCode,
|
|
171
176
|
currentTaskTitle: snapshot.currentTaskTitle,
|
|
172
177
|
summary: snapshot.summary,
|
|
178
|
+
systemPrompt: snapshot.systemPrompt,
|
|
173
179
|
};
|
|
174
180
|
return JSON.stringify(options);
|
|
175
181
|
}
|
|
@@ -231,6 +237,8 @@ function parseSessionOptions(raw) {
|
|
|
231
237
|
options.currentTaskTitle = parsed.currentTaskTitle;
|
|
232
238
|
if (typeof parsed.summary === "string")
|
|
233
239
|
options.summary = parsed.summary;
|
|
240
|
+
if (typeof parsed.systemPrompt === "string")
|
|
241
|
+
options.systemPrompt = parsed.systemPrompt;
|
|
234
242
|
return options;
|
|
235
243
|
}
|
|
236
244
|
function parseQueuedMessages(raw) {
|
|
@@ -772,7 +780,76 @@ const INIT_SQL = `
|
|
|
772
780
|
FOREIGN KEY (session_id) REFERENCES command_sessions(id) ON DELETE CASCADE
|
|
773
781
|
);
|
|
774
782
|
CREATE INDEX IF NOT EXISTS idx_wand_task_sessions_session ON wand_task_sessions(session_id);
|
|
783
|
+
|
|
784
|
+
CREATE TABLE IF NOT EXISTS ai_teams (
|
|
785
|
+
id TEXT PRIMARY KEY,
|
|
786
|
+
name TEXT NOT NULL,
|
|
787
|
+
description TEXT NOT NULL DEFAULT '',
|
|
788
|
+
instructions TEXT NOT NULL DEFAULT '',
|
|
789
|
+
members_json TEXT NOT NULL,
|
|
790
|
+
require_plan_approval INTEGER NOT NULL DEFAULT 1,
|
|
791
|
+
max_steps INTEGER NOT NULL DEFAULT 30,
|
|
792
|
+
created_at TEXT NOT NULL,
|
|
793
|
+
updated_at TEXT NOT NULL
|
|
794
|
+
);
|
|
795
|
+
|
|
796
|
+
CREATE TABLE IF NOT EXISTS ai_team_runs (
|
|
797
|
+
id TEXT PRIMARY KEY,
|
|
798
|
+
team_id TEXT NOT NULL,
|
|
799
|
+
task_id TEXT NOT NULL,
|
|
800
|
+
team_json TEXT NOT NULL,
|
|
801
|
+
objective TEXT NOT NULL,
|
|
802
|
+
cwd TEXT NOT NULL,
|
|
803
|
+
status TEXT NOT NULL,
|
|
804
|
+
status_detail TEXT NOT NULL DEFAULT '',
|
|
805
|
+
steps_used INTEGER NOT NULL DEFAULT 0,
|
|
806
|
+
step_limit INTEGER NOT NULL,
|
|
807
|
+
format_retries INTEGER NOT NULL DEFAULT 0,
|
|
808
|
+
plan_approved INTEGER NOT NULL DEFAULT 0,
|
|
809
|
+
created_at TEXT NOT NULL,
|
|
810
|
+
updated_at TEXT NOT NULL
|
|
811
|
+
);
|
|
812
|
+
CREATE INDEX IF NOT EXISTS idx_ai_team_runs_task ON ai_team_runs(task_id, created_at);
|
|
813
|
+
CREATE INDEX IF NOT EXISTS idx_ai_team_runs_status ON ai_team_runs(status);
|
|
814
|
+
|
|
815
|
+
CREATE TABLE IF NOT EXISTS ai_team_steps (
|
|
816
|
+
id TEXT PRIMARY KEY,
|
|
817
|
+
run_id TEXT NOT NULL,
|
|
818
|
+
seq INTEGER NOT NULL,
|
|
819
|
+
kind TEXT NOT NULL,
|
|
820
|
+
member_id TEXT NOT NULL,
|
|
821
|
+
title TEXT NOT NULL,
|
|
822
|
+
instructions TEXT NOT NULL,
|
|
823
|
+
session_id TEXT,
|
|
824
|
+
status TEXT NOT NULL,
|
|
825
|
+
report TEXT NOT NULL DEFAULT '',
|
|
826
|
+
report_path TEXT NOT NULL,
|
|
827
|
+
depends_on_json TEXT NOT NULL DEFAULT '[]',
|
|
828
|
+
started_at TEXT,
|
|
829
|
+
ended_at TEXT,
|
|
830
|
+
UNIQUE (run_id, seq)
|
|
831
|
+
);
|
|
832
|
+
CREATE INDEX IF NOT EXISTS idx_ai_team_steps_session ON ai_team_steps(session_id, status);
|
|
775
833
|
`;
|
|
834
|
+
/** AI 团队表的后加列;只加不删,历史行取默认值。 */
|
|
835
|
+
function ensureAiTeamSchema(db) {
|
|
836
|
+
const teamColumns = new Set(db.prepare("PRAGMA table_info(ai_teams)").all().map((column) => column.name));
|
|
837
|
+
if (!teamColumns.has("instructions"))
|
|
838
|
+
db.exec("ALTER TABLE ai_teams ADD COLUMN instructions TEXT NOT NULL DEFAULT ''");
|
|
839
|
+
const stepColumns = new Set(db.prepare("PRAGMA table_info(ai_team_steps)").all().map((column) => column.name));
|
|
840
|
+
if (!stepColumns.has("depends_on_json"))
|
|
841
|
+
db.exec("ALTER TABLE ai_team_steps ADD COLUMN depends_on_json TEXT NOT NULL DEFAULT '[]'");
|
|
842
|
+
const runColumns = new Set(db.prepare("PRAGMA table_info(ai_team_runs)").all().map((column) => column.name));
|
|
843
|
+
if (!runColumns.has("chat_session_id"))
|
|
844
|
+
db.exec("ALTER TABLE ai_team_runs ADD COLUMN chat_session_id TEXT");
|
|
845
|
+
if (!runColumns.has("pending_notes_json"))
|
|
846
|
+
db.exec("ALTER TABLE ai_team_runs ADD COLUMN pending_notes_json TEXT NOT NULL DEFAULT '[]'");
|
|
847
|
+
// v2 多候选降级:步骤实际候选与跳过记录(StepDispatchInfo)、run 级状态(黑名单等)。只加列,旧行取默认 '{}'。
|
|
848
|
+
if (!stepColumns.has("dispatch_info_json"))
|
|
849
|
+
db.exec("ALTER TABLE ai_team_steps ADD COLUMN dispatch_info_json TEXT NOT NULL DEFAULT '{}'");
|
|
850
|
+
if (!runColumns.has("run_state_json"))
|
|
851
|
+
db.exec("ALTER TABLE ai_team_runs ADD COLUMN run_state_json TEXT NOT NULL DEFAULT '{}'");
|
|
852
|
+
}
|
|
776
853
|
function ensureWandTaskSchema(db) {
|
|
777
854
|
const columns = db.prepare("PRAGMA table_info(wand_tasks)").all();
|
|
778
855
|
const names = new Set(columns.map((column) => column.name));
|
|
@@ -903,6 +980,7 @@ export function ensureDatabaseFile(dbPath) {
|
|
|
903
980
|
ensureWandTaskSchema(db);
|
|
904
981
|
ensureWandMilestoneSchema(db);
|
|
905
982
|
ensureIterationPromptSchema(db);
|
|
983
|
+
ensureAiTeamSchema(db);
|
|
906
984
|
ensureConnectorSchema(db);
|
|
907
985
|
{
|
|
908
986
|
const missionColumns = db.prepare("PRAGMA table_info(missions)").all();
|
|
@@ -935,6 +1013,7 @@ export class WandStorage {
|
|
|
935
1013
|
ensureWandTaskSchema(this.db);
|
|
936
1014
|
ensureWandMilestoneSchema(this.db);
|
|
937
1015
|
ensureIterationPromptSchema(this.db);
|
|
1016
|
+
ensureAiTeamSchema(this.db);
|
|
938
1017
|
ensureConnectorSchema(this.db);
|
|
939
1018
|
this.ensureDefaultPasswordVault();
|
|
940
1019
|
}
|
|
@@ -1197,6 +1276,22 @@ export class WandStorage {
|
|
|
1197
1276
|
.all();
|
|
1198
1277
|
return new Map(rows.map((row) => [row.id, Number(row.n) || 0]));
|
|
1199
1278
|
}
|
|
1279
|
+
/** Count user-initiated CLI launches (one per session), including archived sessions. */
|
|
1280
|
+
countInteractiveSessionsByProvider() {
|
|
1281
|
+
const counts = Object.fromEntries(SESSION_PROVIDERS.map((provider) => [provider, 0]));
|
|
1282
|
+
const rows = this.db.prepare(`SELECT provider, runner, command, COUNT(*) AS n
|
|
1283
|
+
FROM command_sessions
|
|
1284
|
+
WHERE session_source = 'interactive'
|
|
1285
|
+
GROUP BY provider, runner, command`).all();
|
|
1286
|
+
for (const row of rows) {
|
|
1287
|
+
const provider = isSessionProvider(row.provider)
|
|
1288
|
+
? row.provider
|
|
1289
|
+
: inferProviderFromRunner(row.runner) ?? inferProviderFromCommand(row.command);
|
|
1290
|
+
if (provider)
|
|
1291
|
+
counts[provider] += Number(row.n) || 0;
|
|
1292
|
+
}
|
|
1293
|
+
return counts;
|
|
1294
|
+
}
|
|
1200
1295
|
/** Sessions not yet attached to a project. Omits messages/output. */
|
|
1201
1296
|
listUnboundSessionBindings() {
|
|
1202
1297
|
const rows = this.db
|
|
@@ -1870,6 +1965,126 @@ export class WandStorage {
|
|
|
1870
1965
|
deleteExpiredAuthSessions(now) {
|
|
1871
1966
|
this.db.prepare("DELETE FROM auth_sessions WHERE expires_at < ?").run(now);
|
|
1872
1967
|
}
|
|
1968
|
+
// ============ AI Teams ============
|
|
1969
|
+
listAiTeams() {
|
|
1970
|
+
const rows = this.db.prepare("SELECT * FROM ai_teams ORDER BY created_at ASC").all();
|
|
1971
|
+
return rows.map(mapAiTeamRow);
|
|
1972
|
+
}
|
|
1973
|
+
getAiTeam(id) {
|
|
1974
|
+
const row = this.db.prepare("SELECT * FROM ai_teams WHERE id = ?").get(id);
|
|
1975
|
+
return row ? mapAiTeamRow(row) : null;
|
|
1976
|
+
}
|
|
1977
|
+
saveAiTeam(team) {
|
|
1978
|
+
this.db.prepare(`INSERT INTO ai_teams (
|
|
1979
|
+
id, name, description, instructions, members_json, require_plan_approval, max_steps,
|
|
1980
|
+
created_at, updated_at
|
|
1981
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1982
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
1983
|
+
name = excluded.name, description = excluded.description,
|
|
1984
|
+
instructions = excluded.instructions,
|
|
1985
|
+
members_json = excluded.members_json,
|
|
1986
|
+
require_plan_approval = excluded.require_plan_approval,
|
|
1987
|
+
max_steps = excluded.max_steps, updated_at = excluded.updated_at`).run(team.id, team.name, team.description, team.instructions, JSON.stringify(team.members), team.requirePlanApproval ? 1 : 0, team.maxSteps, team.createdAt, team.updatedAt);
|
|
1988
|
+
}
|
|
1989
|
+
/** 只删团队定义;已有运行保存了团队快照,不受影响。 */
|
|
1990
|
+
deleteAiTeam(id) {
|
|
1991
|
+
this.db.prepare("DELETE FROM ai_teams WHERE id = ?").run(id);
|
|
1992
|
+
}
|
|
1993
|
+
saveAiTeamRun(run) {
|
|
1994
|
+
this.db.prepare(`INSERT INTO ai_team_runs (
|
|
1995
|
+
id, team_id, task_id, team_json, objective, cwd, status, status_detail,
|
|
1996
|
+
steps_used, step_limit, format_retries, plan_approved, chat_session_id, pending_notes_json,
|
|
1997
|
+
created_at, updated_at
|
|
1998
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1999
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
2000
|
+
team_json = excluded.team_json,
|
|
2001
|
+
status = excluded.status, status_detail = excluded.status_detail,
|
|
2002
|
+
steps_used = excluded.steps_used, step_limit = excluded.step_limit,
|
|
2003
|
+
format_retries = excluded.format_retries, plan_approved = excluded.plan_approved,
|
|
2004
|
+
chat_session_id = excluded.chat_session_id, pending_notes_json = excluded.pending_notes_json,
|
|
2005
|
+
updated_at = excluded.updated_at`).run(run.id, run.teamId, run.taskId, JSON.stringify(run.team), run.objective, run.cwd, run.status, run.statusDetail, run.stepsUsed, run.stepLimit, run.formatRetries, run.planApproved ? 1 : 0, run.chatSessionId, JSON.stringify(run.pendingNotes), run.createdAt, run.updatedAt);
|
|
2006
|
+
}
|
|
2007
|
+
/** 群聊会话对应的最近一次运行(同一个群聊可以接着开新运行)。 */
|
|
2008
|
+
getLatestAiTeamRunByChat(sessionId) {
|
|
2009
|
+
const row = this.db.prepare("SELECT * FROM ai_team_runs WHERE chat_session_id = ? ORDER BY created_at DESC LIMIT 1").get(sessionId);
|
|
2010
|
+
return row ? mapAiTeamRunRow(row) : null;
|
|
2011
|
+
}
|
|
2012
|
+
/**
|
|
2013
|
+
* 会话列表用的群聊标记索引:chat_session_id → 最近一次运行的入口信息。
|
|
2014
|
+
* 一次查询建整张表,供 /api/tasks 每请求一次构建;不逐会话查询。
|
|
2015
|
+
*/
|
|
2016
|
+
listAiTeamRunChatMarkers() {
|
|
2017
|
+
const rows = this.db.prepare(`SELECT id, team_json, chat_session_id FROM ai_team_runs
|
|
2018
|
+
WHERE chat_session_id IS NOT NULL AND chat_session_id <> '' ORDER BY created_at DESC`).all();
|
|
2019
|
+
const markers = new Map();
|
|
2020
|
+
for (const row of rows) {
|
|
2021
|
+
const chatSessionId = typeof row.chat_session_id === "string" ? row.chat_session_id : "";
|
|
2022
|
+
if (!chatSessionId || markers.has(chatSessionId))
|
|
2023
|
+
continue;
|
|
2024
|
+
const rawTeam = safeJsonParse(typeof row.team_json === "string" ? row.team_json : null);
|
|
2025
|
+
markers.set(chatSessionId, {
|
|
2026
|
+
runId: String(row.id),
|
|
2027
|
+
teamName: String(rawTeam?.name ?? ""),
|
|
2028
|
+
memberCount: Array.isArray(rawTeam?.members) ? rawTeam.members.length : 0,
|
|
2029
|
+
});
|
|
2030
|
+
}
|
|
2031
|
+
return markers;
|
|
2032
|
+
}
|
|
2033
|
+
getAiTeamRun(id) {
|
|
2034
|
+
const row = this.db.prepare("SELECT * FROM ai_team_runs WHERE id = ?").get(id);
|
|
2035
|
+
return row ? mapAiTeamRunRow(row) : null;
|
|
2036
|
+
}
|
|
2037
|
+
listAiTeamRuns(filter = {}) {
|
|
2038
|
+
const where = [];
|
|
2039
|
+
const params = [];
|
|
2040
|
+
if (filter.taskId) {
|
|
2041
|
+
where.push("task_id = ?");
|
|
2042
|
+
params.push(filter.taskId);
|
|
2043
|
+
}
|
|
2044
|
+
if (filter.teamId) {
|
|
2045
|
+
where.push("team_id = ?");
|
|
2046
|
+
params.push(filter.teamId);
|
|
2047
|
+
}
|
|
2048
|
+
if (filter.statuses) {
|
|
2049
|
+
if (filter.statuses.length === 0)
|
|
2050
|
+
return [];
|
|
2051
|
+
where.push(`status IN (${filter.statuses.map(() => "?").join(", ")})`);
|
|
2052
|
+
params.push(...filter.statuses);
|
|
2053
|
+
}
|
|
2054
|
+
const rows = this.db.prepare(`SELECT * FROM ai_team_runs ${where.length ? `WHERE ${where.join(" AND ")}` : ""} ORDER BY created_at DESC`
|
|
2055
|
+
+ (filter.limit ? ` LIMIT ${Math.max(1, Math.floor(filter.limit))}` : "")).all(...params);
|
|
2056
|
+
return rows.map(mapAiTeamRunRow);
|
|
2057
|
+
}
|
|
2058
|
+
saveAiTeamStep(step) {
|
|
2059
|
+
this.db.prepare(`INSERT INTO ai_team_steps (
|
|
2060
|
+
id, run_id, seq, kind, member_id, title, instructions, session_id, status,
|
|
2061
|
+
report, report_path, depends_on_json, started_at, ended_at, dispatch_info_json
|
|
2062
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2063
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
2064
|
+
session_id = excluded.session_id, status = excluded.status, report = excluded.report,
|
|
2065
|
+
instructions = excluded.instructions,
|
|
2066
|
+
depends_on_json = excluded.depends_on_json,
|
|
2067
|
+
dispatch_info_json = excluded.dispatch_info_json,
|
|
2068
|
+
started_at = excluded.started_at, ended_at = excluded.ended_at`).run(step.id, step.runId, step.seq, step.kind, step.memberId, step.title, step.instructions, step.sessionId, step.status, step.report, step.reportPath, JSON.stringify(step.dependsOn), step.startedAt, step.endedAt, JSON.stringify(step.dispatchInfo ?? { usedCandidate: 0, skipped: [] }));
|
|
2069
|
+
}
|
|
2070
|
+
listAiTeamSteps(runId) {
|
|
2071
|
+
const rows = this.db.prepare("SELECT * FROM ai_team_steps WHERE run_id = ? ORDER BY seq ASC").all(runId);
|
|
2072
|
+
return rows.map(mapAiTeamStepRow);
|
|
2073
|
+
}
|
|
2074
|
+
getRunningAiTeamStepBySession(sessionId) {
|
|
2075
|
+
const row = this.db.prepare("SELECT * FROM ai_team_steps WHERE session_id = ? AND status = 'running' LIMIT 1").get(sessionId);
|
|
2076
|
+
return row ? mapAiTeamStepRow(row) : null;
|
|
2077
|
+
}
|
|
2078
|
+
/** `ai_team_runs.run_state_json` 的读侧入口(§3.4);脏值 / 旧行退化成空黑名单。 */
|
|
2079
|
+
getAiTeamRunState(runId) {
|
|
2080
|
+
const row = this.db.prepare("SELECT run_state_json FROM ai_team_runs WHERE id = ?").get(runId);
|
|
2081
|
+
return parseAiTeamRunState(row?.run_state_json);
|
|
2082
|
+
}
|
|
2083
|
+
/** 只写 `run_state_json` 一列,不碰 `saveAiTeamRun` 负责的业务字段。 */
|
|
2084
|
+
setAiTeamRunState(runId, state) {
|
|
2085
|
+
this.db.prepare("UPDATE ai_team_runs SET run_state_json = ? WHERE id = ?")
|
|
2086
|
+
.run(JSON.stringify(state), runId);
|
|
2087
|
+
}
|
|
1873
2088
|
// ============ Missions ============
|
|
1874
2089
|
saveMission(mission) {
|
|
1875
2090
|
this.db.prepare(`INSERT INTO missions (
|
|
@@ -2065,6 +2280,202 @@ export class WandStorage {
|
|
|
2065
2280
|
this.db.prepare("DELETE FROM command_sessions WHERE id = ?").run(id);
|
|
2066
2281
|
}
|
|
2067
2282
|
}
|
|
2283
|
+
function mapAiTeamRow(row) {
|
|
2284
|
+
return {
|
|
2285
|
+
id: String(row.id),
|
|
2286
|
+
name: String(row.name),
|
|
2287
|
+
description: String(row.description ?? ""),
|
|
2288
|
+
instructions: String(row.instructions ?? ""),
|
|
2289
|
+
members: normalizeAiTeamMembers(safeJsonParse(typeof row.members_json === "string" ? row.members_json : null)),
|
|
2290
|
+
requirePlanApproval: Number(row.require_plan_approval) !== 0,
|
|
2291
|
+
maxSteps: Number(row.max_steps) || AI_TEAM_DEFAULT_MAX_STEPS,
|
|
2292
|
+
createdAt: String(row.created_at),
|
|
2293
|
+
updatedAt: String(row.updated_at),
|
|
2294
|
+
};
|
|
2295
|
+
}
|
|
2296
|
+
/**
|
|
2297
|
+
* 旧行读端归一(§3.1 修正 B10):members 是从 members_json/team_json 解析出的裸 JSON,
|
|
2298
|
+
* 不满足 AiTeamMember 类型,必须逐成员经 memberAgents 补齐 agents 后才能当 AiTeamMember 用。
|
|
2299
|
+
* 没有任何可识别执行配置的成员行直接丢弃(旧数据必然带 agent,走到这里说明行已损坏)。
|
|
2300
|
+
*/
|
|
2301
|
+
function normalizeAiTeamMember(raw) {
|
|
2302
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
2303
|
+
return null;
|
|
2304
|
+
const value = raw;
|
|
2305
|
+
const rawAgent = parseWandTaskAgent(value.agent) ?? undefined;
|
|
2306
|
+
const agents = memberAgents({
|
|
2307
|
+
agent: rawAgent,
|
|
2308
|
+
agents: Array.isArray(value.agents)
|
|
2309
|
+
? value.agents.map((candidate) => parseWandTaskAgent(candidate)).filter((candidate) => candidate !== null)
|
|
2310
|
+
: undefined,
|
|
2311
|
+
});
|
|
2312
|
+
if (agents.length === 0)
|
|
2313
|
+
return null;
|
|
2314
|
+
const member = {
|
|
2315
|
+
id: String(value.id ?? ""),
|
|
2316
|
+
name: String(value.name ?? ""),
|
|
2317
|
+
duty: String(value.duty ?? ""),
|
|
2318
|
+
agents,
|
|
2319
|
+
agent: rawAgent ?? agents[0],
|
|
2320
|
+
isLeader: value.isLeader === true,
|
|
2321
|
+
};
|
|
2322
|
+
if (typeof value.avatar === "string" && value.avatar)
|
|
2323
|
+
member.avatar = value.avatar;
|
|
2324
|
+
if (isTeamMemberRole(value.role))
|
|
2325
|
+
member.role = value.role;
|
|
2326
|
+
return member;
|
|
2327
|
+
}
|
|
2328
|
+
function normalizeAiTeamMembers(raw) {
|
|
2329
|
+
if (!Array.isArray(raw))
|
|
2330
|
+
return [];
|
|
2331
|
+
const members = [];
|
|
2332
|
+
for (const item of raw) {
|
|
2333
|
+
const member = normalizeAiTeamMember(item);
|
|
2334
|
+
if (member)
|
|
2335
|
+
members.push(member);
|
|
2336
|
+
}
|
|
2337
|
+
return members;
|
|
2338
|
+
}
|
|
2339
|
+
function mapAiTeamRunRow(row) {
|
|
2340
|
+
const rawTeam = safeJsonParse(typeof row.team_json === "string" ? row.team_json : null);
|
|
2341
|
+
const team = rawTeam ? {
|
|
2342
|
+
id: String(rawTeam.id ?? row.team_id),
|
|
2343
|
+
name: String(rawTeam.name ?? ""),
|
|
2344
|
+
description: String(rawTeam.description ?? ""),
|
|
2345
|
+
instructions: String(rawTeam.instructions ?? ""),
|
|
2346
|
+
members: normalizeAiTeamMembers(rawTeam.members),
|
|
2347
|
+
requirePlanApproval: rawTeam.requirePlanApproval === undefined ? true : rawTeam.requirePlanApproval !== false,
|
|
2348
|
+
maxSteps: Number(rawTeam.maxSteps) || AI_TEAM_DEFAULT_MAX_STEPS,
|
|
2349
|
+
createdAt: String(rawTeam.createdAt ?? row.created_at),
|
|
2350
|
+
updatedAt: String(rawTeam.updatedAt ?? row.created_at),
|
|
2351
|
+
} : null;
|
|
2352
|
+
return {
|
|
2353
|
+
id: String(row.id),
|
|
2354
|
+
teamId: String(row.team_id),
|
|
2355
|
+
team: team ?? {
|
|
2356
|
+
id: String(row.team_id), name: "", description: "", instructions: "", members: [], requirePlanApproval: true,
|
|
2357
|
+
maxSteps: AI_TEAM_DEFAULT_MAX_STEPS, createdAt: String(row.created_at), updatedAt: String(row.created_at),
|
|
2358
|
+
},
|
|
2359
|
+
taskId: String(row.task_id),
|
|
2360
|
+
objective: String(row.objective),
|
|
2361
|
+
cwd: String(row.cwd),
|
|
2362
|
+
status: String(row.status),
|
|
2363
|
+
statusDetail: String(row.status_detail ?? ""),
|
|
2364
|
+
stepsUsed: Number(row.steps_used) || 0,
|
|
2365
|
+
stepLimit: Number(row.step_limit) || AI_TEAM_DEFAULT_MAX_STEPS,
|
|
2366
|
+
formatRetries: Number(row.format_retries) || 0,
|
|
2367
|
+
planApproved: Number(row.plan_approved) !== 0,
|
|
2368
|
+
chatSessionId: typeof row.chat_session_id === "string" && row.chat_session_id ? row.chat_session_id : null,
|
|
2369
|
+
pendingNotes: (safeJsonParse(typeof row.pending_notes_json === "string" ? row.pending_notes_json : null) ?? [])
|
|
2370
|
+
.filter((note) => typeof note === "string"),
|
|
2371
|
+
createdAt: String(row.created_at),
|
|
2372
|
+
updatedAt: String(row.updated_at),
|
|
2373
|
+
};
|
|
2374
|
+
}
|
|
2375
|
+
/** 候选失败类别的读侧白名单;真源是 `ai-team-types.ts` 的 `CandidateFailureKind`。 */
|
|
2376
|
+
const CANDIDATE_FAILURE_KINDS = [
|
|
2377
|
+
"spawn-missing", "host-disabled", "model-unknown", "startup-timeout",
|
|
2378
|
+
"runtime-failure", "format-error", "user-stop",
|
|
2379
|
+
];
|
|
2380
|
+
function isCandidateFailureKind(value) {
|
|
2381
|
+
return CANDIDATE_FAILURE_KINDS.includes(value);
|
|
2382
|
+
}
|
|
2383
|
+
function nonEmptyString(value) {
|
|
2384
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
2385
|
+
}
|
|
2386
|
+
/**
|
|
2387
|
+
* `ai_team_steps.dispatch_info_json` 的读侧校验(§3.2)。脏值 / 旧行默认 `{}` 一律退化成
|
|
2388
|
+
* 「首选、没跳过任何候选」,坏条目逐条丢弃,不整字段作废——降级留痕是可观测性数据,
|
|
2389
|
+
* 不能因为一条脏记录把整条链丢掉。
|
|
2390
|
+
*/
|
|
2391
|
+
export function parseStepDispatchInfo(raw) {
|
|
2392
|
+
const value = typeof raw === "string" ? safeJsonParse(raw) : raw;
|
|
2393
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
2394
|
+
return { usedCandidate: 0, skipped: [] };
|
|
2395
|
+
const record = value;
|
|
2396
|
+
const skippedRaw = Array.isArray(record.skipped) ? record.skipped : [];
|
|
2397
|
+
const skipped = [];
|
|
2398
|
+
for (const item of skippedRaw) {
|
|
2399
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
2400
|
+
continue;
|
|
2401
|
+
const entry = item;
|
|
2402
|
+
const agent = parseWandTaskAgent(entry.agent);
|
|
2403
|
+
const candidate = Number(entry.candidate);
|
|
2404
|
+
const reason = nonEmptyString(entry.reason);
|
|
2405
|
+
if (!agent || !Number.isInteger(candidate) || candidate < 0 || !reason)
|
|
2406
|
+
continue;
|
|
2407
|
+
// 未知类别按 runtime-failure 记:它不可降级,宁可少降一级也不因为脏数据错换候选。
|
|
2408
|
+
skipped.push({
|
|
2409
|
+
candidate,
|
|
2410
|
+
agent,
|
|
2411
|
+
reason,
|
|
2412
|
+
errorKind: isCandidateFailureKind(entry.errorKind) ? entry.errorKind : "runtime-failure",
|
|
2413
|
+
});
|
|
2414
|
+
}
|
|
2415
|
+
const usedRaw = Number(record.usedCandidate);
|
|
2416
|
+
const used = Number.isInteger(usedRaw) && usedRaw > 0 ? usedRaw : 0;
|
|
2417
|
+
// 与留痕自相矛盾的脏值以 skipped 链为准向下纠正,绝不凭空跳级。
|
|
2418
|
+
const trail = skipped.reduce((highest, item) => Math.max(highest, item.candidate), -1) + 1;
|
|
2419
|
+
return { usedCandidate: skipped.length > 0 ? Math.min(used, trail) : used, skipped };
|
|
2420
|
+
}
|
|
2421
|
+
/** `ai_team_runs.run_state_json` 的读侧校验(§3.4);脏值退化成空黑名单。 */
|
|
2422
|
+
export function parseAiTeamRunState(raw) {
|
|
2423
|
+
const value = typeof raw === "string" ? safeJsonParse(raw) : raw;
|
|
2424
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
2425
|
+
return { providers: [], agents: [], strikes: {}, hostDisabled: [] };
|
|
2426
|
+
const record = value;
|
|
2427
|
+
const state = { providers: [], agents: [], strikes: {}, hostDisabled: [] };
|
|
2428
|
+
for (const item of Array.isArray(record.providers) ? record.providers : []) {
|
|
2429
|
+
const provider = nonEmptyString(item);
|
|
2430
|
+
if (isSessionProvider(provider) && !state.providers.includes(provider))
|
|
2431
|
+
state.providers.push(provider);
|
|
2432
|
+
}
|
|
2433
|
+
for (const item of Array.isArray(record.agents) ? record.agents : []) {
|
|
2434
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
2435
|
+
continue;
|
|
2436
|
+
const entry = item;
|
|
2437
|
+
const key = nonEmptyString(entry.key);
|
|
2438
|
+
if (key && !state.agents.some((agent) => agent.key === key)) {
|
|
2439
|
+
state.agents.push({ key, kind: isCandidateFailureKind(entry.kind) ? entry.kind : "runtime-failure" });
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
const strikes = record.strikes;
|
|
2443
|
+
if (strikes && typeof strikes === "object" && !Array.isArray(strikes)) {
|
|
2444
|
+
for (const [key, count] of Object.entries(strikes)) {
|
|
2445
|
+
const value2 = Number(count);
|
|
2446
|
+
if (key && Number.isFinite(value2) && value2 > 0)
|
|
2447
|
+
state.strikes[key] = Math.min(999, Math.floor(value2));
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
for (const item of Array.isArray(record.hostDisabled) ? record.hostDisabled : []) {
|
|
2451
|
+
const kind = nonEmptyString(item);
|
|
2452
|
+
if (isWandTaskAgentKind(kind) && !state.hostDisabled.includes(kind))
|
|
2453
|
+
state.hostDisabled.push(kind);
|
|
2454
|
+
}
|
|
2455
|
+
return state;
|
|
2456
|
+
}
|
|
2457
|
+
function mapAiTeamStepRow(row) {
|
|
2458
|
+
const step = {
|
|
2459
|
+
id: String(row.id),
|
|
2460
|
+
runId: String(row.run_id),
|
|
2461
|
+
seq: Number(row.seq),
|
|
2462
|
+
kind: String(row.kind),
|
|
2463
|
+
memberId: String(row.member_id),
|
|
2464
|
+
title: String(row.title),
|
|
2465
|
+
instructions: String(row.instructions),
|
|
2466
|
+
sessionId: typeof row.session_id === "string" ? row.session_id : null,
|
|
2467
|
+
status: String(row.status),
|
|
2468
|
+
report: String(row.report ?? ""),
|
|
2469
|
+
reportPath: String(row.report_path),
|
|
2470
|
+
dependsOn: safeJsonParse(typeof row.depends_on_json === "string" ? row.depends_on_json : null) ?? [],
|
|
2471
|
+
startedAt: typeof row.started_at === "string" ? row.started_at : null,
|
|
2472
|
+
endedAt: typeof row.ended_at === "string" ? row.ended_at : null,
|
|
2473
|
+
};
|
|
2474
|
+
const dispatchInfo = parseStepDispatchInfo(typeof row.dispatch_info_json === "string" ? row.dispatch_info_json : null);
|
|
2475
|
+
if (dispatchInfo.usedCandidate > 0 || dispatchInfo.skipped.length > 0)
|
|
2476
|
+
step.dispatchInfo = dispatchInfo;
|
|
2477
|
+
return step;
|
|
2478
|
+
}
|
|
2068
2479
|
function mapMissionRow(row) {
|
|
2069
2480
|
return {
|
|
2070
2481
|
id: String(row.id),
|
|
@@ -8,6 +8,11 @@ export interface PermissionPolicy {
|
|
|
8
8
|
}
|
|
9
9
|
export declare function derivePermissionPolicy(mode: ExecutionMode, autoApprove: boolean, cwd: string): PermissionPolicy;
|
|
10
10
|
export declare function buildAppendSystemPromptParts(language: string | undefined, mode: ExecutionMode): string[];
|
|
11
|
+
/**
|
|
12
|
+
* 完整 append 列表:模式 / 语言指令 + 会话级系统提示(团队、自动化会话的角色与规则)。
|
|
13
|
+
* CLI 与 SDK 两条路径共用,避免只改一边。
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildSessionSystemPromptParts(session: Pick<SessionSnapshot, "mode" | "systemPrompt">, language: string | undefined): string[];
|
|
11
16
|
export interface ClaudeCliArgsOptions {
|
|
12
17
|
permissionPolicy: PermissionPolicy;
|
|
13
18
|
systemPromptParts?: string[];
|
|
@@ -92,6 +92,17 @@ export function buildAppendSystemPromptParts(language, mode) {
|
|
|
92
92
|
}
|
|
93
93
|
return parts;
|
|
94
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* 完整 append 列表:模式 / 语言指令 + 会话级系统提示(团队、自动化会话的角色与规则)。
|
|
97
|
+
* CLI 与 SDK 两条路径共用,避免只改一边。
|
|
98
|
+
*/
|
|
99
|
+
export function buildSessionSystemPromptParts(session, language) {
|
|
100
|
+
const parts = buildAppendSystemPromptParts(language, session.mode);
|
|
101
|
+
const sessionPrompt = session.systemPrompt?.trim();
|
|
102
|
+
if (sessionPrompt)
|
|
103
|
+
parts.push(sessionPrompt);
|
|
104
|
+
return parts;
|
|
105
|
+
}
|
|
95
106
|
export function buildClaudeCliArgs(session, options) {
|
|
96
107
|
const args = ["-p", "--verbose", "--output-format", "stream-json"];
|
|
97
108
|
if (options.permissionPolicy.permissionMode !== "default") {
|
|
@@ -129,7 +140,7 @@ export class ClaudeCliRunner {
|
|
|
129
140
|
const permissionPolicy = derivePermissionPolicy(context.session.mode, context.session.autoApprovePermissions ?? false, context.session.cwd);
|
|
130
141
|
const args = buildClaudeCliArgs(context.session, {
|
|
131
142
|
permissionPolicy,
|
|
132
|
-
systemPromptParts:
|
|
143
|
+
systemPromptParts: buildSessionSystemPromptParts(context.session, this.options.language?.()),
|
|
133
144
|
});
|
|
134
145
|
const reducer = new ClaudeCliProtocolReducer(context.session);
|
|
135
146
|
let killedForQuestion = false;
|
|
@@ -179,7 +190,7 @@ export class ClaudeCliRunner {
|
|
|
179
190
|
signal,
|
|
180
191
|
stderr: ctx.stderr,
|
|
181
192
|
stdoutTail,
|
|
182
|
-
primaryError:
|
|
193
|
+
primaryError: reducer.errorText,
|
|
183
194
|
stopReason: killedForQuestion ? "ask-user-question" : undefined,
|
|
184
195
|
spawnError,
|
|
185
196
|
}),
|
|
@@ -20,6 +20,8 @@ interface ClaudeCliTurnState extends StructuredRunnerTurnState {
|
|
|
20
20
|
export declare class ClaudeCliProtocolReducer {
|
|
21
21
|
readonly state: ClaudeCliTurnState;
|
|
22
22
|
askUserQuestionDetected: boolean;
|
|
23
|
+
/** result 事件带 is_error 时的说明(如「API Error: 503 …」),作为这一轮的主错误。 */
|
|
24
|
+
errorText: string | null;
|
|
23
25
|
private readonly blocksByKey;
|
|
24
26
|
private readonly keyOrder;
|
|
25
27
|
private readonly taskMetaRegistry;
|
|
@@ -121,6 +121,8 @@ export function extractClaudeAssistantMessage(message) {
|
|
|
121
121
|
export class ClaudeCliProtocolReducer {
|
|
122
122
|
state;
|
|
123
123
|
askUserQuestionDetected = false;
|
|
124
|
+
/** result 事件带 is_error 时的说明(如「API Error: 503 …」),作为这一轮的主错误。 */
|
|
125
|
+
errorText = null;
|
|
124
126
|
blocksByKey = new Map();
|
|
125
127
|
keyOrder = [];
|
|
126
128
|
taskMetaRegistry = new Map();
|
|
@@ -186,6 +188,8 @@ export class ClaudeCliProtocolReducer {
|
|
|
186
188
|
if (event.type === "result") {
|
|
187
189
|
if (typeof event.result === "string")
|
|
188
190
|
this.state.result = event.result.trim();
|
|
191
|
+
if (event.is_error === true && this.state.result)
|
|
192
|
+
this.errorText = this.state.result;
|
|
189
193
|
this.state.model = extractClaudeModelName(event.modelUsage && typeof event.modelUsage === "object"
|
|
190
194
|
? event.modelUsage
|
|
191
195
|
: undefined) ?? this.state.model;
|
|
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { startStructuredCli } from "./structured-exec-pump.js";
|
|
3
3
|
import { CodexProtocolReducer } from "./structured-codex-protocol.js";
|
|
4
4
|
import { whichSync } from "./path-repair.js";
|
|
5
|
-
import { thinkingEffortToCodexReasoningEffort } from "./structured-provider-common.js";
|
|
5
|
+
import { thinkingEffortToCodexReasoningEffort, promptWithSystemFallback } from "./structured-provider-common.js";
|
|
6
6
|
/** Build the stable CLI contract for a structured Codex turn. */
|
|
7
7
|
export function buildCodexArgs(session) {
|
|
8
8
|
const args = ["exec", "--json", "--color", "never"];
|
|
@@ -49,7 +49,7 @@ export class CodexRunner {
|
|
|
49
49
|
args,
|
|
50
50
|
cwd: context.session.cwd,
|
|
51
51
|
env: context.env,
|
|
52
|
-
stdinData: context.prompt,
|
|
52
|
+
stdinData: promptWithSystemFallback(context.session, context.prompt),
|
|
53
53
|
observer,
|
|
54
54
|
execHost: this.execHost,
|
|
55
55
|
spawnProcess: this.spawnProcess,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { startStructuredCli } from "./structured-exec-pump.js";
|
|
3
3
|
import { asRecord } from "./structured-content.js";
|
|
4
|
-
import { thinkingEffortToGrokEffort } from "./structured-provider-common.js";
|
|
4
|
+
import { systemPromptArgs, thinkingEffortToGrokEffort } from "./structured-provider-common.js";
|
|
5
5
|
function grokText(value) {
|
|
6
6
|
if (typeof value === "string")
|
|
7
7
|
return value;
|
|
@@ -147,6 +147,7 @@ export function buildGrokArgs(session, prompt) {
|
|
|
147
147
|
}
|
|
148
148
|
if (session.claudeSessionId)
|
|
149
149
|
args.push("--resume", session.claudeSessionId);
|
|
150
|
+
args.push(...systemPromptArgs(session));
|
|
150
151
|
return args;
|
|
151
152
|
}
|
|
152
153
|
/** Apply one official Grok Build `streaming-json` event. */
|
|
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { startStructuredCli } from "./structured-exec-pump.js";
|
|
4
4
|
import { asRecord } from "./structured-content.js";
|
|
5
|
-
import { thinkingEffortToOpenCodeVariant } from "./structured-provider-common.js";
|
|
5
|
+
import { thinkingEffortToOpenCodeVariant, promptWithSystemFallback } from "./structured-provider-common.js";
|
|
6
6
|
function extractStructuredText(value) {
|
|
7
7
|
if (typeof value === "string")
|
|
8
8
|
return value;
|
|
@@ -135,7 +135,7 @@ export class OpenCodeRunner {
|
|
|
135
135
|
args,
|
|
136
136
|
cwd: context.session.cwd,
|
|
137
137
|
env: context.env,
|
|
138
|
-
stdinData: context.prompt,
|
|
138
|
+
stdinData: promptWithSystemFallback(context.session, context.prompt),
|
|
139
139
|
observer,
|
|
140
140
|
execHost: this.execHost,
|
|
141
141
|
spawnProcess: this.spawnProcess,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { startStructuredCli } from "./structured-exec-pump.js";
|
|
3
3
|
import { asRecord, normalizeStructuredToolResultContent } from "./structured-content.js";
|
|
4
|
-
import { thinkingEffortToPiLevel } from "./structured-provider-common.js";
|
|
4
|
+
import { systemPromptArgs, thinkingEffortToPiLevel } from "./structured-provider-common.js";
|
|
5
5
|
export function isMissingPiSession(error, sessionId) {
|
|
6
6
|
return !!sessionId && !!error?.includes(`No session found matching '${sessionId}'`);
|
|
7
7
|
}
|
|
@@ -41,6 +41,7 @@ export function buildPiArgs(session, prompt) {
|
|
|
41
41
|
const thinking = thinkingEffortToPiLevel(session.thinkingEffort);
|
|
42
42
|
if (thinking)
|
|
43
43
|
args.push("--thinking", thinking);
|
|
44
|
+
args.push(...systemPromptArgs(session));
|
|
44
45
|
if (session.claudeSessionId)
|
|
45
46
|
args.push("--session", session.claudeSessionId);
|
|
46
47
|
args.push(prompt);
|
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
import type { SessionProvider, SessionRunner, SessionSnapshot, StructuredSessionState, WandConfig } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* 把会话级系统提示交给 CLI 的开关。Claude / Qoder / Pi 用 `--append-system-prompt`,
|
|
4
|
+
* Grok 用 `--rules`;Codex / OpenCode 没有这个入口,只能退回并入本轮消息。
|
|
5
|
+
* 各 provider 的支持情况由 `--help` 实测得出,新增 provider 前先验。
|
|
6
|
+
*/
|
|
7
|
+
export declare function systemPromptFlag(provider: SessionProvider | null | undefined): string | null;
|
|
8
|
+
/** 结构化 runner 的 args:provider 支持时把系统提示当成独立参数传。 */
|
|
9
|
+
export declare function systemPromptArgs(session: Pick<SessionSnapshot, "provider" | "systemPrompt">): string[];
|
|
10
|
+
/**
|
|
11
|
+
* provider 没有系统提示通道时的兜底:把它放在本会话第一条消息最前面,并写明它不是用户输入。
|
|
12
|
+
* 只在首轮拼一次:后续轮次的消息已经接在有它的对话历史后面。有通道的 provider 不会走这里。
|
|
13
|
+
*/
|
|
14
|
+
export declare function promptWithSystemFallback(session: Pick<SessionSnapshot, "provider" | "systemPrompt" | "messages">, prompt: string): string;
|
|
15
|
+
/** provider 没有系统提示通道时的并接格式:明确标出这不是用户输入。 */
|
|
16
|
+
export declare function composeSystemFallback(systemPrompt: string, prompt: string): string;
|
|
17
|
+
/** 把系统提示接到 CLI 命令上(provider 支持时);不支持时原样返回,由调用方并进首条输入。 */
|
|
18
|
+
export declare function commandWithSystemPrompt(command: string, provider: SessionProvider | null | undefined, systemPrompt: string | null | undefined): string;
|
|
2
19
|
/** 判断任意值是否为合法的 thinking effort(旧四档,或 `provider:level`)。 */
|
|
3
20
|
export declare function isThinkingEffort(value: unknown): value is NonNullable<SessionSnapshot["thinkingEffort"]>;
|
|
4
21
|
/** 只取本 provider 的原生档位。别的 CLI 的前缀不能串过去。 */
|