@akira-tl/forgerelay 0.7.2 → 0.7.4
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 +11 -0
- package/capabilities/subagents/GUIDE.md +2 -0
- package/dist/cli.js +3 -1
- package/dist/db/migrations.js +10 -0
- package/dist/db/schema.js +2 -0
- package/dist/server.js +1 -0
- package/dist/subagents/sessions/capability.js +60 -1
- package/dist/subagents/sessions/manager.js +39 -6
- package/dist/subagents/sessions/mcp/runtime.js +1 -0
- package/dist/subagents/sessions/store.js +20 -3
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@ All notable ForgeRelay changes are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.7.4] - 2026-08-30
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Subagent Run restart reconciliation now preserves live owners and marks stale active Runs `interrupted` without replaying the old prompt.
|
|
12
|
+
- First-class Subagent Sessions now work through Workspace Relay and Composite explicit-member routing while keeping Session state on the Execution ForgeRelay.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Composite Capability result remapping now preserves the declared output schema during relayed Subagent operations.
|
|
17
|
+
|
|
7
18
|
## [0.7.2] - 2026-08-30
|
|
8
19
|
|
|
9
20
|
### Added
|
|
@@ -103,6 +103,8 @@ Run 完成后:
|
|
|
103
103
|
- 成功领取后 mailbox 条目立即删除,同一个 completion 不重复交付;
|
|
104
104
|
- 未领取 completion 可以跨正常 ForgeRelay 进程重启保留。
|
|
105
105
|
|
|
106
|
+
ForgeRelay 只为 active Run 持久化最小 execution-owner 元数据(owner identity / PID),不保存 delegated prompt。后续 Session 操作会先做 restart reconciliation:能够证明 owner 仍存活的 Run 保持 `running`;无法证明仍有执行 owner 的 Run 一次性转为 `interrupted`,Session 回到 `idle`。ForgeRelay 绝不自动 replay 被中断 Run 的旧 prompt;如果 provider continuation 仍有效,由 Host 显式 `resume` 新 prompt。
|
|
107
|
+
|
|
106
108
|
如果需要等待结果,使用 `status` 进行有节制的后续查询;不要高频短轮询。
|
|
107
109
|
|
|
108
110
|
## 数据所有权
|
package/dist/cli.js
CHANGED
|
@@ -580,7 +580,8 @@ function createCliSubagentSessionManager(config = loadConfig()) {
|
|
|
580
580
|
return new SubagentSessionManager(config, {
|
|
581
581
|
launch(request) {
|
|
582
582
|
const promptFile = writeSubagentPromptFile(request.prompt);
|
|
583
|
-
spawnSubagentWorker(request.sessionId, promptFile);
|
|
583
|
+
const pid = spawnSubagentWorker(request.sessionId, promptFile);
|
|
584
|
+
return pid === undefined ? undefined : { id: `subagent-worker-${request.runId}`, pid };
|
|
584
585
|
},
|
|
585
586
|
});
|
|
586
587
|
}
|
|
@@ -599,6 +600,7 @@ function spawnSubagentWorker(sessionId, promptFile) {
|
|
|
599
600
|
env: process.env,
|
|
600
601
|
});
|
|
601
602
|
child.unref();
|
|
603
|
+
return child.pid;
|
|
602
604
|
}
|
|
603
605
|
function writeSubagentPromptFile(prompt) {
|
|
604
606
|
const directory = mkdtempSync(join(tmpdir(), "forgerelay-agent-prompt-"));
|
package/dist/db/migrations.js
CHANGED
|
@@ -69,6 +69,11 @@ const migrations = [
|
|
|
69
69
|
name: "subagent-session-coordination",
|
|
70
70
|
up: migrateSubagentSessionCoordination,
|
|
71
71
|
},
|
|
72
|
+
{
|
|
73
|
+
version: 15,
|
|
74
|
+
name: "subagent-run-ownership",
|
|
75
|
+
up: migrateSubagentRunOwnership,
|
|
76
|
+
},
|
|
72
77
|
];
|
|
73
78
|
export function migrateDatabase(sqlite) {
|
|
74
79
|
const migrate = sqlite.transaction(() => {
|
|
@@ -358,6 +363,11 @@ function migrateSubagentSessionCoordination(sqlite) {
|
|
|
358
363
|
addColumnIfMissing(sqlite, "local_agent_sessions", "latest_run_outcome", "text");
|
|
359
364
|
addColumnIfMissing(sqlite, "local_agent_sessions", "latest_run_finished_at", "text");
|
|
360
365
|
}
|
|
366
|
+
function migrateSubagentRunOwnership(sqlite) {
|
|
367
|
+
migrateSubagentSessionCoordination(sqlite);
|
|
368
|
+
addColumnIfMissing(sqlite, "local_agent_sessions", "active_owner_id", "text");
|
|
369
|
+
addColumnIfMissing(sqlite, "local_agent_sessions", "active_owner_pid", "integer");
|
|
370
|
+
}
|
|
361
371
|
function migrateActivityHostTurnWorkspace(sqlite) {
|
|
362
372
|
migrateActivityHostTurns(sqlite);
|
|
363
373
|
addColumnIfMissing(sqlite, "activity_host_turns", "workspace_id", "text");
|
package/dist/db/schema.js
CHANGED
|
@@ -155,6 +155,8 @@ export const localAgentSessions = sqliteTable("local_agent_sessions", {
|
|
|
155
155
|
activeRunId: text("active_run_id"),
|
|
156
156
|
activeActivityId: text("active_activity_id"),
|
|
157
157
|
activeRunStartedAt: text("active_run_started_at"),
|
|
158
|
+
activeOwnerId: text("active_owner_id"),
|
|
159
|
+
activeOwnerPid: integer("active_owner_pid"),
|
|
158
160
|
latestRunId: text("latest_run_id"),
|
|
159
161
|
latestRunOutcome: text("latest_run_outcome"),
|
|
160
162
|
latestRunFinishedAt: text("latest_run_finished_at"),
|
package/dist/server.js
CHANGED
|
@@ -2764,6 +2764,7 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
2764
2764
|
outputSchema: {
|
|
2765
2765
|
name: z.string(),
|
|
2766
2766
|
action: z.enum(["describe", "run"]),
|
|
2767
|
+
member: z.string().optional(),
|
|
2767
2768
|
capability: z.unknown().optional(),
|
|
2768
2769
|
result: z.unknown().optional(),
|
|
2769
2770
|
error: capabilityErrorOutputSchema.optional(),
|
|
@@ -9,18 +9,23 @@ export class SubagentSessionCapability {
|
|
|
9
9
|
activityLifecycle;
|
|
10
10
|
mailbox;
|
|
11
11
|
providerRunner;
|
|
12
|
+
ownerAliveOverride;
|
|
12
13
|
activeRuns = new Map();
|
|
13
14
|
constructor(config, activityLifecycle, options = {}) {
|
|
14
15
|
this.config = config;
|
|
15
16
|
this.activityLifecycle = activityLifecycle;
|
|
16
17
|
this.mailbox = new SubagentDeliveryMailbox(config.stateDir);
|
|
17
18
|
this.providerRunner = options.providerRunner ?? defaultProviderRunner;
|
|
19
|
+
this.ownerAliveOverride = options.ownerAlive;
|
|
18
20
|
}
|
|
19
21
|
async run(input, context, options) {
|
|
20
22
|
const manager = new SubagentSessionManager(this.config, {
|
|
21
23
|
launch: (request) => this.launch(request),
|
|
22
24
|
});
|
|
23
25
|
try {
|
|
26
|
+
const reconciled = manager.reconcile({ workspaceId: context.workspaceId }, (run) => this.ownerAlive(run));
|
|
27
|
+
for (const entry of reconciled)
|
|
28
|
+
this.recordInterruption(entry, options.activityId);
|
|
24
29
|
switch (input.operation) {
|
|
25
30
|
case "start": {
|
|
26
31
|
const started = await manager.start({
|
|
@@ -118,17 +123,19 @@ export class SubagentSessionCapability {
|
|
|
118
123
|
};
|
|
119
124
|
}
|
|
120
125
|
launch(request) {
|
|
126
|
+
const ownerId = `subagent-owner-${process.pid}-${request.runId}`;
|
|
121
127
|
const controller = new AbortController();
|
|
122
128
|
const completion = executeSubagentRun(this.config, { ...request, signal: controller.signal }, this.providerRunner).then((result) => {
|
|
123
129
|
this.recordCompletion(result);
|
|
124
130
|
return result;
|
|
125
131
|
});
|
|
126
|
-
this.activeRuns.set(request.runId, { controller, completion });
|
|
132
|
+
this.activeRuns.set(request.runId, { ownerId, controller, completion });
|
|
127
133
|
void completion.finally(() => {
|
|
128
134
|
this.activeRuns.delete(request.runId);
|
|
129
135
|
}).catch(() => {
|
|
130
136
|
// Unexpected orchestration failures surface through later reconciliation.
|
|
131
137
|
});
|
|
138
|
+
return { id: ownerId, pid: process.pid };
|
|
132
139
|
}
|
|
133
140
|
async stop(manager, sessionId, workspaceId) {
|
|
134
141
|
let session = manager.get(sessionId, { workspaceId });
|
|
@@ -158,6 +165,58 @@ export class SubagentSessionCapability {
|
|
|
158
165
|
...(session.latestRun?.id === activeRun.id ? { run: publicRun(session.latestRun) } : {}),
|
|
159
166
|
};
|
|
160
167
|
}
|
|
168
|
+
ownerAlive(run) {
|
|
169
|
+
if (this.ownerAliveOverride)
|
|
170
|
+
return this.ownerAliveOverride(run);
|
|
171
|
+
if (!run.ownerId || run.ownerPid === undefined)
|
|
172
|
+
return false;
|
|
173
|
+
const active = this.activeRuns.get(run.id);
|
|
174
|
+
if (run.ownerPid === process.pid)
|
|
175
|
+
return active?.ownerId === run.ownerId;
|
|
176
|
+
try {
|
|
177
|
+
process.kill(run.ownerPid, 0);
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
return error.code === "EPERM";
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
recordInterruption(entry, fallbackActivityId) {
|
|
185
|
+
const sourceActivityId = entry.run.activityId ?? fallbackActivityId;
|
|
186
|
+
if (!sourceActivityId)
|
|
187
|
+
return;
|
|
188
|
+
const record = () => this.activityLifecycle.recordLinked({
|
|
189
|
+
sourceActivityId,
|
|
190
|
+
tool: "subagent_result",
|
|
191
|
+
request: { sessionId: entry.session.id, runId: entry.run.id },
|
|
192
|
+
result: {
|
|
193
|
+
sessionId: entry.session.id,
|
|
194
|
+
runId: entry.run.id,
|
|
195
|
+
provider: entry.session.provider,
|
|
196
|
+
status: "interrupted",
|
|
197
|
+
},
|
|
198
|
+
outcome: { type: "failed", error: "Subagent Run interrupted." },
|
|
199
|
+
});
|
|
200
|
+
try {
|
|
201
|
+
record();
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
if (!fallbackActivityId || fallbackActivityId === sourceActivityId)
|
|
205
|
+
return;
|
|
206
|
+
this.activityLifecycle.recordLinked({
|
|
207
|
+
sourceActivityId: fallbackActivityId,
|
|
208
|
+
tool: "subagent_result",
|
|
209
|
+
request: { sessionId: entry.session.id, runId: entry.run.id },
|
|
210
|
+
result: {
|
|
211
|
+
sessionId: entry.session.id,
|
|
212
|
+
runId: entry.run.id,
|
|
213
|
+
provider: entry.session.provider,
|
|
214
|
+
status: "interrupted",
|
|
215
|
+
},
|
|
216
|
+
outcome: { type: "failed", error: "Subagent Run interrupted." },
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
161
220
|
recordCompletion(completion) {
|
|
162
221
|
if (!completion.activityId)
|
|
163
222
|
return;
|
|
@@ -52,13 +52,16 @@ export class SubagentSessionManager {
|
|
|
52
52
|
const run = session.activeRun;
|
|
53
53
|
if (!run)
|
|
54
54
|
throw new Error(`Subagent Session ${session.id} did not create an active Run.`);
|
|
55
|
-
this.launcher.launch({
|
|
55
|
+
const owned = this.assignOwner(session, run, this.launcher.launch({
|
|
56
56
|
sessionId: session.id,
|
|
57
57
|
runId,
|
|
58
58
|
...(input.activityId ? { activityId: input.activityId } : {}),
|
|
59
59
|
prompt: input.prompt,
|
|
60
|
-
});
|
|
61
|
-
return {
|
|
60
|
+
}));
|
|
61
|
+
return {
|
|
62
|
+
session: owned,
|
|
63
|
+
run: owned.activeRun ?? (owned.latestRun?.id === run.id ? owned.latestRun : run),
|
|
64
|
+
};
|
|
62
65
|
}
|
|
63
66
|
resume(input, scope = {}) {
|
|
64
67
|
const existing = this.store.getInScope(input.sessionId, scope);
|
|
@@ -90,13 +93,38 @@ export class SubagentSessionManager {
|
|
|
90
93
|
status: "running",
|
|
91
94
|
activeRun: run,
|
|
92
95
|
});
|
|
93
|
-
this.launcher.launch({
|
|
96
|
+
const owned = this.assignOwner(session, run, this.launcher.launch({
|
|
94
97
|
sessionId: session.id,
|
|
95
98
|
runId,
|
|
96
99
|
...(input.activityId ? { activityId: input.activityId } : {}),
|
|
97
100
|
prompt: input.prompt,
|
|
98
|
-
});
|
|
99
|
-
return {
|
|
101
|
+
}));
|
|
102
|
+
return {
|
|
103
|
+
session: owned,
|
|
104
|
+
run: owned.activeRun ?? (owned.latestRun?.id === run.id ? owned.latestRun : run),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
reconcile(scope, ownerAlive) {
|
|
108
|
+
const reconciled = [];
|
|
109
|
+
for (const session of this.store.list(scope)) {
|
|
110
|
+
const run = session.activeRun;
|
|
111
|
+
if (!run || ownerAlive(run))
|
|
112
|
+
continue;
|
|
113
|
+
const interrupted = {
|
|
114
|
+
id: run.id,
|
|
115
|
+
status: "interrupted",
|
|
116
|
+
...(run.activityId ? { activityId: run.activityId } : {}),
|
|
117
|
+
...(run.startedAt ? { startedAt: run.startedAt } : {}),
|
|
118
|
+
finishedAt: new Date().toISOString(),
|
|
119
|
+
};
|
|
120
|
+
const updated = this.store.update(session.id, {
|
|
121
|
+
status: "idle",
|
|
122
|
+
activeRun: undefined,
|
|
123
|
+
latestRun: interrupted,
|
|
124
|
+
});
|
|
125
|
+
reconciled.push({ session: updated, run: interrupted });
|
|
126
|
+
}
|
|
127
|
+
return reconciled;
|
|
100
128
|
}
|
|
101
129
|
delete(sessionId, scope = {}) {
|
|
102
130
|
const session = this.store.getInScope(sessionId, scope);
|
|
@@ -112,6 +140,11 @@ export class SubagentSessionManager {
|
|
|
112
140
|
close() {
|
|
113
141
|
this.store.close();
|
|
114
142
|
}
|
|
143
|
+
assignOwner(session, run, owner) {
|
|
144
|
+
if (!owner)
|
|
145
|
+
return session;
|
|
146
|
+
return this.store.assignActiveRunOwner(session.id, run.id, owner);
|
|
147
|
+
}
|
|
115
148
|
}
|
|
116
149
|
function newRunId() {
|
|
117
150
|
return `run_${randomUUID().replaceAll("-", "").slice(0, 12)}`;
|
|
@@ -3,6 +3,7 @@ export function createSubagentMcpRuntime(config, activityLifecycle, options = {}
|
|
|
3
3
|
const capability = config.subagents
|
|
4
4
|
? new SubagentSessionCapability(config, activityLifecycle, {
|
|
5
5
|
providerRunner: options.subagentProviderRunner,
|
|
6
|
+
ownerAlive: options.subagentOwnerAlive,
|
|
6
7
|
})
|
|
7
8
|
: undefined;
|
|
8
9
|
return {
|
|
@@ -47,6 +47,8 @@ export class SubagentSessionStore {
|
|
|
47
47
|
status: "running",
|
|
48
48
|
...(input.activeRun.activityId ? { activityId: input.activeRun.activityId } : {}),
|
|
49
49
|
startedAt: input.activeRun.startedAt,
|
|
50
|
+
...(input.activeRun.ownerId ? { ownerId: input.activeRun.ownerId } : {}),
|
|
51
|
+
...(input.activeRun.ownerPid !== undefined ? { ownerPid: input.activeRun.ownerPid } : {}),
|
|
50
52
|
},
|
|
51
53
|
}
|
|
52
54
|
: {}),
|
|
@@ -67,6 +69,8 @@ export class SubagentSessionStore {
|
|
|
67
69
|
active_run_id,
|
|
68
70
|
active_activity_id,
|
|
69
71
|
active_run_started_at,
|
|
72
|
+
active_owner_id,
|
|
73
|
+
active_owner_pid,
|
|
70
74
|
latest_run_id,
|
|
71
75
|
latest_run_outcome,
|
|
72
76
|
latest_run_finished_at,
|
|
@@ -75,8 +79,8 @@ export class SubagentSessionStore {
|
|
|
75
79
|
hook_reports_json,
|
|
76
80
|
created_at,
|
|
77
81
|
updated_at
|
|
78
|
-
) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, null, null, null, ?, ?)`)
|
|
79
|
-
.run(record.id, record.workspaceId ?? null, record.workspaceRoot, record.profileName, record.provider, record.model ?? null, record.thinking ?? null, null, record.status, record.activeRun?.id ?? null, record.activeRun?.activityId ?? null, record.activeRun?.startedAt ?? null, null, null, null, record.createdAt, record.updatedAt);
|
|
82
|
+
) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, null, null, null, ?, ?)`)
|
|
83
|
+
.run(record.id, record.workspaceId ?? null, record.workspaceRoot, record.profileName, record.provider, record.model ?? null, record.thinking ?? null, null, record.status, record.activeRun?.id ?? null, record.activeRun?.activityId ?? null, record.activeRun?.startedAt ?? null, record.activeRun?.ownerId ?? null, record.activeRun?.ownerPid ?? null, null, null, null, record.createdAt, record.updatedAt);
|
|
80
84
|
return record;
|
|
81
85
|
}
|
|
82
86
|
get(idOrPrefix) {
|
|
@@ -126,6 +130,8 @@ export class SubagentSessionStore {
|
|
|
126
130
|
active_run_id = ?,
|
|
127
131
|
active_activity_id = ?,
|
|
128
132
|
active_run_started_at = ?,
|
|
133
|
+
active_owner_id = ?,
|
|
134
|
+
active_owner_pid = ?,
|
|
129
135
|
latest_run_id = ?,
|
|
130
136
|
latest_run_outcome = ?,
|
|
131
137
|
latest_run_finished_at = ?,
|
|
@@ -134,9 +140,18 @@ export class SubagentSessionStore {
|
|
|
134
140
|
hook_reports_json = null,
|
|
135
141
|
updated_at = ?
|
|
136
142
|
where id = ?`)
|
|
137
|
-
.run(updated.workspaceId ?? null, resolve(updated.workspaceRoot), updated.profileName, updated.provider, updated.model ?? null, updated.thinking ?? null, updated.providerSessionId ?? null, updated.status, updated.activeRun?.id ?? null, updated.activeRun?.activityId ?? null, updated.activeRun?.startedAt ?? null, updated.latestRun?.id ?? null, updated.latestRun && updated.latestRun.status !== "running" ? updated.latestRun.status : null, updated.latestRun?.finishedAt ?? null, updated.updatedAt, updated.id);
|
|
143
|
+
.run(updated.workspaceId ?? null, resolve(updated.workspaceRoot), updated.profileName, updated.provider, updated.model ?? null, updated.thinking ?? null, updated.providerSessionId ?? null, updated.status, updated.activeRun?.id ?? null, updated.activeRun?.activityId ?? null, updated.activeRun?.startedAt ?? null, updated.activeRun?.ownerId ?? null, updated.activeRun?.ownerPid ?? null, updated.latestRun?.id ?? null, updated.latestRun && updated.latestRun.status !== "running" ? updated.latestRun.status : null, updated.latestRun?.finishedAt ?? null, updated.updatedAt, updated.id);
|
|
138
144
|
return updated;
|
|
139
145
|
}
|
|
146
|
+
assignActiveRunOwner(id, runId, owner) {
|
|
147
|
+
this.database.sqlite.prepare(`update local_agent_sessions
|
|
148
|
+
set active_owner_id = ?, active_owner_pid = ?, updated_at = ?
|
|
149
|
+
where id = ? and active_run_id = ?`).run(owner.id, owner.pid ?? null, new Date().toISOString(), id, runId);
|
|
150
|
+
const current = this.getById(id);
|
|
151
|
+
if (!current)
|
|
152
|
+
throw new Error(`Unknown subagent id: ${id}`);
|
|
153
|
+
return current;
|
|
154
|
+
}
|
|
140
155
|
delete(id) {
|
|
141
156
|
this.database.sqlite.prepare("delete from local_agent_sessions where id = ?").run(id);
|
|
142
157
|
}
|
|
@@ -160,6 +175,8 @@ function rowToSubagentSession(row) {
|
|
|
160
175
|
status: "running",
|
|
161
176
|
...(row.active_activity_id ? { activityId: row.active_activity_id } : {}),
|
|
162
177
|
...(row.active_run_started_at ? { startedAt: row.active_run_started_at } : {}),
|
|
178
|
+
...(row.active_owner_id ? { ownerId: row.active_owner_id } : {}),
|
|
179
|
+
...(row.active_owner_pid !== null ? { ownerPid: row.active_owner_pid } : {}),
|
|
163
180
|
}
|
|
164
181
|
: undefined;
|
|
165
182
|
const latestOutcome = readOutcome(row.latest_run_outcome);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akira-tl/forgerelay",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"description": "Local development control plane for MCP coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://github.com/Akira-TL/forgerelay#readme",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"release:publish": "node scripts/release/publish.mjs",
|
|
48
48
|
"postinstall": "node scripts/fix-node-pty-permissions.mjs",
|
|
49
49
|
"start": "node dist/cli.js serve",
|
|
50
|
-
"test": "node --test scripts/debug/runtime.test.mjs scripts/release-proof.test.mjs scripts/release/release-gate.test.mjs scripts/release/release-version.test.mjs && tsx src/oauth/router.test.ts && tsx src/remote-auth-cli.test.ts && tsx src/remote-ssh-auth-cli.test.ts && tsx src/remote-workspace-relay.test.ts && tsx src/remote-workspace-relay-process.test.ts && tsx src/config.test.ts && tsx src/lsp/language-server-config.test.ts && tsx src/lsp/normalization/hover.test.ts && tsx src/lsp/references.server.test.ts && tsx src/lsp/operations/document-symbols.server.test.ts && tsx src/lsp/operations/workspace-symbols.server.test.ts && tsx src/lsp/operations/diagnostics-push.server.test.ts && tsx src/lsp/operations/diagnostics-pull.server.test.ts && tsx src/lsp/operations/request-hardening.server.test.ts && tsx src/lsp/operations/recovery.server.test.ts && tsx src/lsp/operations/lifecycle.server.test.ts && tsx src/lsp/runtime/semantic-requests.test.ts && tsx src/logger.test.ts && tsx src/proxy-trust.test.ts && tsx src/mcp-app-template.test.ts && tsx src/hooks.test.ts && tsx src/capability-registry.test.ts && tsx src/mcp/server-instructions.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/workspace-lifecycle-app.test.ts && tsx src/ui/activity/model.test.ts && tsx src/ui/patch-display.test.ts && tsx src/ui/tool-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions.test.ts && tsx src/mcp-sessions.test.ts && tsx src/server-shutdown.test.ts && tsx src/subagents/providers/adapters/codex.test.ts && tsx src/subagents/providers/registry.test.ts && tsx src/subagents/providers/availability.test.ts && tsx src/subagents/profiles.test.ts && tsx src/subagents/cli-target.test.ts && tsx src/subagents/sessions/store.test.ts && tsx src/subagents/sessions/manager.test.ts && tsx src/subagents/sessions/mcp/capability.server.test.ts && tsx src/subagents/sessions/mcp/continuation.server.test.ts && tsx src/subagents/sessions/mcp/lifecycle.server.test.ts && tsx src/roots.test.ts && tsx src/file-mutations.test.ts && tsx src/operations/edit-preflight.test.ts && tsx src/skills.test.ts && tsx src/db/migrations.test.ts && tsx src/workspace-store.test.ts && tsx src/activity/audit-store.test.ts && tsx src/activity/bash-output-store.test.ts && tsx src/activity/lifecycle.test.ts && tsx src/activity/query-service.test.ts && tsx src/operations/core-operation-executor.test.ts && tsx src/operations/bulk-mutation.test.ts && tsx src/operations/batch/scheduler.test.ts && tsx src/operations/batch/executor-policy.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/lsp/code-intelligence.server.test.ts && npm run build:app && tsx src/server.test.ts && tsx src/oauth-store.test.ts && tsx src/cli.test.ts",
|
|
50
|
+
"test": "node --test scripts/debug/runtime.test.mjs scripts/release-proof.test.mjs scripts/release/release-gate.test.mjs scripts/release/release-version.test.mjs && tsx src/oauth/router.test.ts && tsx src/remote-auth-cli.test.ts && tsx src/remote-ssh-auth-cli.test.ts && tsx src/remote-workspace-relay.test.ts && tsx src/remote-workspace-relay-process.test.ts && tsx src/config.test.ts && tsx src/lsp/language-server-config.test.ts && tsx src/lsp/normalization/hover.test.ts && tsx src/lsp/references.server.test.ts && tsx src/lsp/operations/document-symbols.server.test.ts && tsx src/lsp/operations/workspace-symbols.server.test.ts && tsx src/lsp/operations/diagnostics-push.server.test.ts && tsx src/lsp/operations/diagnostics-pull.server.test.ts && tsx src/lsp/operations/request-hardening.server.test.ts && tsx src/lsp/operations/recovery.server.test.ts && tsx src/lsp/operations/lifecycle.server.test.ts && tsx src/lsp/runtime/semantic-requests.test.ts && tsx src/logger.test.ts && tsx src/proxy-trust.test.ts && tsx src/mcp-app-template.test.ts && tsx src/hooks.test.ts && tsx src/capability-registry.test.ts && tsx src/mcp/server-instructions.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/workspace-lifecycle-app.test.ts && tsx src/ui/activity/model.test.ts && tsx src/ui/patch-display.test.ts && tsx src/ui/tool-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions.test.ts && tsx src/mcp-sessions.test.ts && tsx src/server-shutdown.test.ts && tsx src/subagents/providers/adapters/codex.test.ts && tsx src/subagents/providers/registry.test.ts && tsx src/subagents/providers/availability.test.ts && tsx src/subagents/profiles.test.ts && tsx src/subagents/cli-target.test.ts && tsx src/subagents/sessions/store.test.ts && tsx src/subagents/sessions/manager.test.ts && tsx src/subagents/sessions/mcp/capability.server.test.ts && tsx src/subagents/sessions/mcp/continuation.server.test.ts && tsx src/subagents/sessions/mcp/lifecycle.server.test.ts && tsx src/subagents/sessions/mcp/reconciliation.server.test.ts && tsx src/subagents/sessions/mcp/routing.server.test.ts && tsx src/roots.test.ts && tsx src/file-mutations.test.ts && tsx src/operations/edit-preflight.test.ts && tsx src/skills.test.ts && tsx src/db/migrations.test.ts && tsx src/workspace-store.test.ts && tsx src/activity/audit-store.test.ts && tsx src/activity/bash-output-store.test.ts && tsx src/activity/lifecycle.test.ts && tsx src/activity/query-service.test.ts && tsx src/operations/core-operation-executor.test.ts && tsx src/operations/bulk-mutation.test.ts && tsx src/operations/batch/scheduler.test.ts && tsx src/operations/batch/executor-policy.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/lsp/code-intelligence.server.test.ts && npm run build:app && tsx src/server.test.ts && tsx src/oauth-store.test.ts && tsx src/cli.test.ts",
|
|
51
51
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
52
52
|
"release:check": "node scripts/release-version.mjs check",
|
|
53
53
|
"release:tag-check": "node scripts/release-version.mjs tag",
|