@codetreker/daynight 0.21.13 → 0.24.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 +364 -287
- package/config.example.yml +69 -0
- package/dist/api/client.d.ts +2 -2
- package/dist/api/client.js +10 -7
- package/dist/api/client.js.map +1 -1
- package/dist/api/types.d.ts +1 -8
- package/dist/cli/prompts.d.ts +1 -0
- package/dist/commands/auth/credentials.js +1 -1
- package/dist/commands/auth/credentials.js.map +1 -1
- package/dist/commands/review/config.d.ts +2 -1
- package/dist/commands/review/config.js +22 -39
- package/dist/commands/review/config.js.map +1 -1
- package/dist/commands/review/configuration-guide.js +7 -6
- package/dist/commands/review/configuration-guide.js.map +1 -1
- package/dist/commands/review/definition.js +1 -1
- package/dist/commands/review/definition.js.map +1 -1
- package/dist/commands/review/repositories.js +19 -19
- package/dist/commands/review/repositories.js.map +1 -1
- package/dist/commands/skill/skills/using-daynight/references/execute-task-in-managed.md +6 -5
- package/dist/commands/skill/skills/using-daynight/references/handbook.md +1 -1
- package/dist/commands/skill/skills/using-daynight/references/handoff.md +1 -1
- package/dist/commands/worker/attach.d.ts +3 -0
- package/dist/commands/worker/attach.js +53 -0
- package/dist/commands/worker/attach.js.map +1 -0
- package/dist/commands/worker/cleanup.d.ts +8 -2
- package/dist/commands/worker/cleanup.js +44 -160
- package/dist/commands/worker/cleanup.js.map +1 -1
- package/dist/commands/worker/config.d.ts +13 -3
- package/dist/commands/worker/config.js +35 -13
- package/dist/commands/worker/config.js.map +1 -1
- package/dist/commands/worker/configuration-guide.d.ts +19 -0
- package/dist/commands/worker/configuration-guide.js +86 -0
- package/dist/commands/worker/configuration-guide.js.map +1 -0
- package/dist/commands/worker/control.d.ts +11 -18
- package/dist/commands/worker/control.js +75 -132
- package/dist/commands/worker/control.js.map +1 -1
- package/dist/commands/worker/coordinator.d.ts +10 -8
- package/dist/commands/worker/coordinator.js +311 -473
- package/dist/commands/worker/coordinator.js.map +1 -1
- package/dist/commands/worker/definition.js +34 -18
- package/dist/commands/worker/definition.js.map +1 -1
- package/dist/commands/worker/diagnostics.js +2 -3
- package/dist/commands/worker/diagnostics.js.map +1 -1
- package/dist/commands/worker/execution.d.ts +35 -10
- package/dist/commands/worker/execution.js +92 -283
- package/dist/commands/worker/execution.js.map +1 -1
- package/dist/commands/worker/herdr/agents.d.ts +12 -91
- package/dist/commands/worker/herdr/agents.js +111 -358
- package/dist/commands/worker/herdr/agents.js.map +1 -1
- package/dist/commands/worker/herdr/instance.d.ts +1 -0
- package/dist/commands/worker/herdr/instance.js +30 -5
- package/dist/commands/worker/herdr/instance.js.map +1 -1
- package/dist/commands/worker/herdr/preflight.d.ts +21 -9
- package/dist/commands/worker/herdr/preflight.js +118 -124
- package/dist/commands/worker/herdr/preflight.js.map +1 -1
- package/dist/commands/worker/herdr.d.ts +7 -63
- package/dist/commands/worker/herdr.js.map +1 -1
- package/dist/commands/worker/identity.js +2 -2
- package/dist/commands/worker/identity.js.map +1 -1
- package/dist/commands/worker/index.d.ts +8 -0
- package/dist/commands/worker/index.js +21 -14
- package/dist/commands/worker/index.js.map +1 -1
- package/dist/commands/worker/prompts.d.ts +10 -3
- package/dist/commands/worker/prompts.js +21 -32
- package/dist/commands/worker/prompts.js.map +1 -1
- package/dist/commands/worker/remote.d.ts +2 -3
- package/dist/commands/worker/remote.js +1 -6
- package/dist/commands/worker/remote.js.map +1 -1
- package/dist/commands/worker/repositories.d.ts +40 -0
- package/dist/commands/worker/repositories.js +236 -0
- package/dist/commands/worker/repositories.js.map +1 -0
- package/dist/commands/worker/runtime.d.ts +1 -0
- package/dist/commands/worker/runtime.js +135 -212
- package/dist/commands/worker/runtime.js.map +1 -1
- package/dist/commands/worker/setup-environment.d.ts +31 -0
- package/dist/commands/worker/setup-environment.js +146 -0
- package/dist/commands/worker/setup-environment.js.map +1 -0
- package/dist/commands/worker/setup.d.ts +11 -0
- package/dist/commands/worker/setup.js +113 -0
- package/dist/commands/worker/setup.js.map +1 -0
- package/dist/commands/worker/store.d.ts +12 -114
- package/dist/commands/worker/store.js +92 -817
- package/dist/commands/worker/store.js.map +1 -1
- package/dist/commands/worker/types.d.ts +10 -114
- package/dist/commands/worker/worktrees.d.ts +9 -9
- package/dist/commands/worker/worktrees.js +77 -37
- package/dist/commands/worker/worktrees.js.map +1 -1
- package/dist/config/document.d.ts +0 -1
- package/dist/config/document.js +1 -22
- package/dist/config/document.js.map +1 -1
- package/dist/config/errors.d.ts +2 -2
- package/dist/config/errors.js.map +1 -1
- package/dist/config/files.d.ts +7 -1
- package/dist/config/files.js +4 -1
- package/dist/config/files.js.map +1 -1
- package/dist/config/ownership.d.ts +12 -0
- package/dist/config/ownership.js +39 -0
- package/dist/config/ownership.js.map +1 -0
- package/dist/repository/location.d.ts +7 -0
- package/dist/repository/location.js +51 -0
- package/dist/repository/location.js.map +1 -0
- package/package.json +4 -3
|
@@ -27,61 +27,9 @@ const pid = (value) => { if (typeof value !== "number" || !Number.isSafeInteger(
|
|
|
27
27
|
throw new TypeError("Herdr process ID must be a positive process identifier."); return value; };
|
|
28
28
|
const counter = (value, name) => { if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
|
|
29
29
|
throw new TypeError(`${name} must be a nonnegative safe integer.`); return value; };
|
|
30
|
-
const nativeProcessEvidence = (value, agent) => {
|
|
31
|
-
const executable = (candidate) => {
|
|
32
|
-
if (typeof candidate !== "string")
|
|
33
|
-
return false;
|
|
34
|
-
const leaf = candidate.split(/[\\/]/).at(-1)?.toLowerCase();
|
|
35
|
-
return leaf === agent || leaf === `${agent}.exe` || leaf === `${agent}.js`;
|
|
36
|
-
};
|
|
37
|
-
if (value.argv !== undefined) {
|
|
38
|
-
const argv = array(value.argv, "Native process arguments");
|
|
39
|
-
if (argv.some((argument) => typeof argument !== "string"))
|
|
40
|
-
throw new TypeError("Native process arguments must be strings.");
|
|
41
|
-
if (executable(argv[0]))
|
|
42
|
-
return true;
|
|
43
|
-
const interpreter = typeof argv[0] === "string" ? argv[0].split(/[\\/]/).at(-1)?.toLowerCase() : undefined;
|
|
44
|
-
if ((interpreter === "node" || interpreter === "node.exe") && executable(argv[1]))
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
return executable(value.argv0) || executable(value.name);
|
|
48
|
-
};
|
|
49
|
-
const nativeInteractiveProcessEvidence = (value, agent) => {
|
|
50
|
-
if (!Array.isArray(value.argv) || !value.argv.every((argument) => typeof argument === "string"))
|
|
51
|
-
return false;
|
|
52
|
-
const argv = value.argv;
|
|
53
|
-
const leaf = (argument) => argument?.split(/[\\/]/).at(-1)?.toLowerCase();
|
|
54
|
-
const native = (argument) => [agent, `${agent}.exe`, `${agent}.js`].includes(leaf(argument));
|
|
55
|
-
const offset = native(argv[0]) ? 1 : ["node", "node.exe"].includes(leaf(argv[0])) && native(argv[1]) ? 2 : 0;
|
|
56
|
-
if (offset === 0)
|
|
57
|
-
return false;
|
|
58
|
-
const args = argv.slice(offset);
|
|
59
|
-
// Only an interactive invocation can fence deferred input. Subcommands such
|
|
60
|
-
// as `codex exec` and their descendants are not the original UI process.
|
|
61
|
-
if (agent === "codex" && args[0] === "--no-daemon")
|
|
62
|
-
args.shift();
|
|
63
|
-
if (agent === "codex" && args[0] === "resume" && typeof args[1] === "string" && canonicalId(args[1]) !== null)
|
|
64
|
-
args.splice(0, 2);
|
|
65
|
-
if (agent === "copilot" && args[0]?.startsWith("--resume=") && canonicalId(args[0].slice("--resume=".length)) !== null)
|
|
66
|
-
args.shift();
|
|
67
|
-
if (args[0] === "--model" && typeof args[1] === "string" && args[1] !== "")
|
|
68
|
-
args.splice(0, 2);
|
|
69
|
-
try {
|
|
70
|
-
validateHerdrAgentArguments(agent, args);
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
30
|
const expectedResult = (value, type) => { if (value.type !== type)
|
|
78
31
|
throw new TypeError(`Expected Herdr ${type} result; received ${value.type}.`); return value; };
|
|
79
|
-
const sameBinding = (left, right) => left.session === right.session && left.workspaceId === right.workspaceId && left.paneId === right.paneId && left.terminalId === right.terminalId;
|
|
80
32
|
const contextIdentity = (context) => {
|
|
81
|
-
if (context.agent !== "codex" && context.agent !== "copilot")
|
|
82
|
-
throw new TypeError("Execution agent must be codex or copilot.");
|
|
83
|
-
if (!path.isAbsolute(context.cwd) || /[\r\n\0]/.test(context.cwd))
|
|
84
|
-
throw new TypeError("The task cwd must be an absolute path without line breaks or NUL.");
|
|
85
33
|
const identifier = (value, label) => { const result = canonicalId(value); if (result === null)
|
|
86
34
|
throw new TypeError(`${label} must be a UUID.`); return result; };
|
|
87
35
|
return { serverOrigin: normalizeServerUrl(context.serverOrigin), projectId: identifier(context.projectId, "Project ID"), taskId: identifier(context.taskId, "Task ID"), claimId: identifier(context.claimId, "Claim ID"), worktreeId: identifier(context.worktreeId, "Worktree ID") };
|
|
@@ -107,14 +55,12 @@ export class HerdrAgentActionError extends Error {
|
|
|
107
55
|
action;
|
|
108
56
|
submission;
|
|
109
57
|
promptSubmission;
|
|
110
|
-
launchEvidence;
|
|
111
58
|
constructor(message, action, submission, options) {
|
|
112
59
|
super(message, options);
|
|
113
60
|
this.action = action;
|
|
114
61
|
this.submission = submission;
|
|
115
62
|
this.name = "HerdrAgentActionError";
|
|
116
63
|
this.promptSubmission = options?.promptSubmission === undefined ? null : options.promptSubmission;
|
|
117
|
-
this.launchEvidence = options?.launchEvidence === undefined ? null : options.launchEvidence;
|
|
118
64
|
}
|
|
119
65
|
}
|
|
120
66
|
const rejectionCodes = {
|
|
@@ -146,20 +92,12 @@ export const createHerdrAgentManager = (options) => {
|
|
|
146
92
|
if (Object.keys(suppliedEnvironment).length !== xdgFields.length || xdgFields.some((field) => !Object.hasOwn(suppliedEnvironment, field) || typeof suppliedEnvironment[field] !== "string" || suppliedEnvironment[field].includes("\0")))
|
|
147
93
|
throw new TypeError("Task workspace environment must contain only XDG_CONFIG_HOME and XDG_STATE_HOME string values without NUL.");
|
|
148
94
|
const workspaceEnvironment = { XDG_CONFIG_HOME: suppliedEnvironment.XDG_CONFIG_HOME, XDG_STATE_HOME: suppliedEnvironment.XDG_STATE_HOME };
|
|
149
|
-
const validWorkspaceEvidence = (context, binding, evidence) => {
|
|
150
|
-
if (evidence === undefined || evidence === null || typeof evidence.binding !== "object" || evidence.binding === null || !sameBinding(evidence.binding, binding) || evidence.ownerLabel !== herdrExecutionLabel(context))
|
|
151
|
-
return false;
|
|
152
|
-
const environment = evidence.environment;
|
|
153
|
-
if (typeof environment !== "object" || environment === null || Array.isArray(environment))
|
|
154
|
-
return false;
|
|
155
|
-
const values = environment;
|
|
156
|
-
return Object.keys(values).length === xdgFields.length && xdgFields.every((field) => Object.hasOwn(values, field) && values[field] === workspaceEnvironment[field]);
|
|
157
|
-
};
|
|
158
95
|
const isExecuting = options.processExecuting === undefined ? processExecuting : options.processExecuting;
|
|
159
96
|
const pollInterval = options.pollIntervalMs === undefined ? 100 : options.pollIntervalMs;
|
|
160
97
|
if (!Number.isSafeInteger(pollInterval) || pollInterval <= 0 || pollInterval > 1000)
|
|
161
98
|
throw new TypeError("The Herdr observation interval must be between 1 and 1000 milliseconds.");
|
|
162
99
|
const inputTails = new Map();
|
|
100
|
+
const stoppingProcesses = new Map();
|
|
163
101
|
const serialized = async (key, operation, action) => {
|
|
164
102
|
operation.check();
|
|
165
103
|
const previous = inputTails.get(key);
|
|
@@ -196,6 +134,24 @@ export const createHerdrAgentManager = (options) => {
|
|
|
196
134
|
if (operation.signal.aborted)
|
|
197
135
|
abort();
|
|
198
136
|
});
|
|
137
|
+
const finishStop = async (key, operation) => {
|
|
138
|
+
const pending = stoppingProcesses.get(key);
|
|
139
|
+
if (pending === undefined)
|
|
140
|
+
return;
|
|
141
|
+
while (true) {
|
|
142
|
+
let running = false;
|
|
143
|
+
for (const processId of pending.processIds) {
|
|
144
|
+
operation.check();
|
|
145
|
+
if (await isExecuting(processId))
|
|
146
|
+
running = true;
|
|
147
|
+
}
|
|
148
|
+
if (!running) {
|
|
149
|
+
stoppingProcesses.delete(key);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
await wait(operation);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
199
155
|
const snapshot = async (operation) => {
|
|
200
156
|
const result = expectedResult(await options.rpc.request("session.snapshot", {}, operation), "session_snapshot");
|
|
201
157
|
const data = object(result.snapshot, "Herdr snapshot");
|
|
@@ -289,7 +245,7 @@ export const createHerdrAgentManager = (options) => {
|
|
|
289
245
|
let before;
|
|
290
246
|
const verifyInputAgent = (current) => {
|
|
291
247
|
const knownAgent = current.kind === context.agent || current.nativeSession?.agent === context.agent || (initialPrompt && current.name === herdrExecutionAgentName(context));
|
|
292
|
-
if (!knownAgent || current.launchPending || (
|
|
248
|
+
if (!knownAgent || current.launchPending || ((initialPrompt || current.nativeSession === null) && !current.interactiveReady) || current.status === "blocked")
|
|
293
249
|
throw new Error("The original native agent is not ready for input.");
|
|
294
250
|
};
|
|
295
251
|
try {
|
|
@@ -323,72 +279,8 @@ export const createHerdrAgentManager = (options) => {
|
|
|
323
279
|
}
|
|
324
280
|
return { submission: "submitted", observation: before };
|
|
325
281
|
};
|
|
326
|
-
const verifyStopObservation = (context, binding, evidence) => {
|
|
327
|
-
if (!sameBinding(binding, evidence.binding) || evidence.ownerLabel !== herdrExecutionLabel(context) || evidence.agent !== context.agent)
|
|
328
|
-
throw new Error("The saved process observation belongs to another execution.");
|
|
329
|
-
pid(evidence.shellPid);
|
|
330
|
-
if (evidence.nativeSessionId !== null && canonicalId(evidence.nativeSessionId) === null)
|
|
331
|
-
throw new Error("The saved native session identity is invalid.");
|
|
332
|
-
if (!Array.isArray(evidence.processIds) || evidence.processIds.length === 0 || new Set(evidence.processIds).size !== evidence.processIds.length || evidence.processIds.some((value) => pid(value) === evidence.shellPid))
|
|
333
|
-
throw new Error("The original native process observation is invalid.");
|
|
334
|
-
if (evidence.nativeProcessIds !== null && (!Array.isArray(evidence.nativeProcessIds) || evidence.nativeProcessIds.length === 0 || new Set(evidence.nativeProcessIds).size !== evidence.nativeProcessIds.length || evidence.nativeProcessIds.some((value) => pid(value) === evidence.shellPid || !evidence.processIds.includes(value))))
|
|
335
|
-
throw new Error("The original interactive native process observation is invalid.");
|
|
336
|
-
if (!Number.isSafeInteger(evidence.observedAt) || evidence.observedAt < 0)
|
|
337
|
-
throw new Error("The original process observation time is invalid.");
|
|
338
|
-
};
|
|
339
|
-
const absentProcesses = async (evidence, operation) => {
|
|
340
|
-
for (const processId of evidence.processIds) {
|
|
341
|
-
operation.check();
|
|
342
|
-
if (await isExecuting(processId))
|
|
343
|
-
throw new Error("The original native process is still executing or its absence is unconfirmed.");
|
|
344
|
-
}
|
|
345
|
-
};
|
|
346
|
-
const verifyOriginalStopScope = (context, binding, evidence) => {
|
|
347
|
-
verifyStopObservation(context, binding, evidence);
|
|
348
|
-
if (binding.session !== session)
|
|
349
|
-
throw new Error("The original stop observation belongs to another Herdr session.");
|
|
350
|
-
for (const field of ["session", "workspaceId", "paneId", "terminalId"])
|
|
351
|
-
text(binding[field], `Original Herdr ${field}`);
|
|
352
|
-
};
|
|
353
|
-
const originalProcessesAbsent = async (evidence, operation) => {
|
|
354
|
-
await absentProcesses(evidence, operation);
|
|
355
|
-
operation.check();
|
|
356
|
-
if (await isExecuting(evidence.shellPid))
|
|
357
|
-
throw new Error("The original task shell is still executing or its absence is unconfirmed.");
|
|
358
|
-
};
|
|
359
|
-
const requireOriginalResourceAbsent = (data, binding) => {
|
|
360
|
-
const workspaceIds = new Set();
|
|
361
|
-
for (const workspace of data.workspaces) {
|
|
362
|
-
const id = text(workspace.workspace_id, "Observed workspace ID");
|
|
363
|
-
if (typeof workspace.label !== "string" || workspaceIds.has(id))
|
|
364
|
-
throw new Error("The Herdr workspace snapshot is malformed or ambiguous.");
|
|
365
|
-
workspaceIds.add(id);
|
|
366
|
-
if (id === binding.workspaceId)
|
|
367
|
-
throw new Error("The original task workspace is still present.");
|
|
368
|
-
}
|
|
369
|
-
if ([...data.panes, ...data.agents].some((resource) => resource.workspace_id === binding.workspaceId || resource.pane_id === binding.paneId || resource.terminal_id === binding.terminalId))
|
|
370
|
-
throw new Error("The original task pane, terminal or agent is still present in the Herdr snapshot.");
|
|
371
|
-
};
|
|
372
|
-
const emptyOwnedPane = async (context, binding, operation, mode) => {
|
|
373
|
-
const current = await observation(context, binding, operation);
|
|
374
|
-
if (current.kind !== null || current.launchPending || current.interactiveReady || (mode === "unlaunched" && (current.name !== null || current.nativeSession !== null)))
|
|
375
|
-
throw new Error(mode === "unlaunched" ? "The task pane is not an unlaunched plain shell." : "The restored task pane is not an inactive shell.");
|
|
376
|
-
const owned = await readOwned(context, binding, operation);
|
|
377
|
-
if (mode === "unlaunched" && owned.data.panes.filter((pane) => pane.workspace_id === binding.workspaceId).length !== 1)
|
|
378
|
-
throw new Error("The task workspace does not contain a single unlaunched shell.");
|
|
379
|
-
const result = expectedResult(await options.rpc.request("pane.process_info", { pane_id: binding.paneId }, operation), "pane_process_info");
|
|
380
|
-
const info = object(result.process_info, "Empty task pane process observation");
|
|
381
|
-
if (info.pane_id !== binding.paneId)
|
|
382
|
-
throw new Error("The empty-shell process observation belongs to another pane.");
|
|
383
|
-
const shellPid = pid(info.shell_pid);
|
|
384
|
-
if (info.foreground_process_group_id !== undefined && pid(info.foreground_process_group_id) !== shellPid)
|
|
385
|
-
throw new Error("The task shell has a different foreground process group.");
|
|
386
|
-
const processes = info.foreground_processes === undefined ? [] : array(info.foreground_processes, "Empty task foreground processes");
|
|
387
|
-
if (processes.some((value) => pid(object(value, "Empty task foreground process").pid) !== shellPid))
|
|
388
|
-
throw new Error("The task shell still hosts foreground work.");
|
|
389
|
-
return { current, data: owned.data };
|
|
390
|
-
};
|
|
391
282
|
return {
|
|
283
|
+
pendingStops: () => [...stoppingProcesses.values()].map(({ context, binding }) => ({ context: { ...context }, binding: { ...binding } })),
|
|
392
284
|
listResources: async (operation) => {
|
|
393
285
|
const data = await snapshot(operation);
|
|
394
286
|
const result = [];
|
|
@@ -454,7 +346,7 @@ export const createHerdrAgentManager = (options) => {
|
|
|
454
346
|
let createPossible = false;
|
|
455
347
|
try {
|
|
456
348
|
const label = herdrExecutionLabel(context);
|
|
457
|
-
await realpath(context.cwd);
|
|
349
|
+
const expectedCwd = path.normalize(await realpath(context.cwd));
|
|
458
350
|
const data = await snapshot(operation);
|
|
459
351
|
const matching = data.workspaces.filter((workspace) => workspace.label === label);
|
|
460
352
|
if (matching.length > 1)
|
|
@@ -467,260 +359,121 @@ export const createHerdrAgentManager = (options) => {
|
|
|
467
359
|
const pane = panes[0];
|
|
468
360
|
const binding = { session, workspaceId, paneId: text(pane.pane_id, "Task pane ID"), terminalId: text(pane.terminal_id, "Task terminal ID") };
|
|
469
361
|
await readOwned(context, binding, operation);
|
|
470
|
-
return
|
|
362
|
+
return binding;
|
|
471
363
|
}
|
|
472
|
-
const expectedCwd = path.normalize(await realpath(context.cwd));
|
|
473
364
|
for (const pane of data.panes) {
|
|
474
|
-
if (typeof pane.cwd
|
|
475
|
-
continue;
|
|
476
|
-
const reportedCwd = path.normalize(path.resolve(pane.cwd));
|
|
477
|
-
if (reportedCwd === expectedCwd || reportedCwd === path.normalize(path.resolve(context.cwd)))
|
|
365
|
+
if (typeof pane.cwd === "string" && [expectedCwd, path.normalize(path.resolve(context.cwd))].includes(path.normalize(path.resolve(pane.cwd))))
|
|
478
366
|
throw new Error("The requested task cwd already belongs to a different Herdr workspace.");
|
|
479
367
|
}
|
|
480
368
|
createPossible = true;
|
|
481
369
|
const result = await mutation("workspace.create", { cwd: context.cwd, focus: false, label, env: { ...workspaceEnvironment } }, "workspace_created", operation);
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
return { binding, workspaceEvidence: { binding: { ...binding }, ownerLabel: label, environment: { ...workspaceEnvironment } } };
|
|
488
|
-
}
|
|
489
|
-
catch (cause) {
|
|
490
|
-
throw new HerdrAgentActionError("The created task workspace could not be verified.", "workspace.create", "unknown", { cause });
|
|
491
|
-
}
|
|
370
|
+
const workspace = object(result.workspace, "Created workspace");
|
|
371
|
+
const pane = object(result.root_pane, "Created task pane");
|
|
372
|
+
const binding = { session, workspaceId: text(workspace.workspace_id, "Created workspace ID"), paneId: text(pane.pane_id, "Created pane ID"), terminalId: text(pane.terminal_id, "Created terminal ID") };
|
|
373
|
+
await readOwned(context, binding, operation);
|
|
374
|
+
return binding;
|
|
492
375
|
}
|
|
493
376
|
catch (cause) {
|
|
494
|
-
if (
|
|
377
|
+
if (cause instanceof HerdrAgentActionError)
|
|
495
378
|
throw cause;
|
|
496
379
|
throw new HerdrAgentActionError(cause instanceof Error ? cause.message : "The task workspace could not be prepared.", "workspace.create", createPossible ? "unknown" : "not-submitted", { cause });
|
|
497
380
|
}
|
|
498
381
|
},
|
|
499
382
|
observe: observation,
|
|
500
|
-
start:
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
383
|
+
start: (context, binding, input, operation) => serialized(binding.workspaceId, operation, async () => {
|
|
384
|
+
if (input.configuration.agent !== context.agent)
|
|
385
|
+
throw new TypeError("The agent configuration differs from the execution identity.");
|
|
386
|
+
const args = buildHerdrAgentArguments(input.configuration, input.resumeSessionId);
|
|
387
|
+
if (typeof input.prompt !== "string" || input.prompt.length === 0)
|
|
388
|
+
throw new TypeError("The startup prompt must be nonempty text.");
|
|
389
|
+
const before = await observation(context, binding, operation);
|
|
390
|
+
if (before.kind !== null || before.launchPending || before.interactiveReady)
|
|
391
|
+
return before;
|
|
392
|
+
await options.preflight.verifyStart({ context, configuration: input.configuration, ...(input.resumeSessionId === undefined ? {} : { resumeSessionId: input.resumeSessionId }) }, operation);
|
|
393
|
+
const timeout = Math.floor(Math.min(30_000, operation.remainingMs()));
|
|
394
|
+
if (timeout <= 3000)
|
|
395
|
+
throw new HerdrAgentActionError("The remaining operation budget cannot start a native agent.", "agent.start", "not-submitted");
|
|
396
|
+
const started = await mutation("agent.start", { name: herdrExecutionAgentName(context), kind: context.agent, pane_id: binding.paneId, args, timeout_ms: timeout }, "agent_started", operation);
|
|
397
|
+
const acknowledged = object(started.agent, "Started native agent");
|
|
398
|
+
const argv = array(started.argv, "Native launch arguments");
|
|
399
|
+
const expectedArgv = [context.agent, ...args];
|
|
400
|
+
if (acknowledged.pane_id !== binding.paneId || acknowledged.terminal_id !== binding.terminalId || acknowledged.workspace_id !== binding.workspaceId || argv.length !== expectedArgv.length || argv.some((value, index) => value !== expectedArgv[index]))
|
|
401
|
+
throw new HerdrAgentActionError("The native start result differs from the requested target or arguments.", "agent.start", "unknown");
|
|
504
402
|
try {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
if (input.
|
|
510
|
-
throw new
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const existing = before.kind !== null || before.name !== null || before.launchPending || before.interactiveReady;
|
|
516
|
-
if (input.resumeSessionId !== undefined && before.nativeSession !== null && before.nativeSession.sessionId !== canonicalId(input.resumeSessionId))
|
|
517
|
-
throw new Error("The retained native session differs from the original recovery session.");
|
|
518
|
-
const rebind = (reason) => ({ kind: "rebind-required", reason, observation: before, promptSubmission: "not-submitted", launchEvidence: null });
|
|
519
|
-
if (!before.launchPending && !before.interactiveReady && before.kind === null && before.nativeSession !== null)
|
|
520
|
-
return rebind("retained-native-reference");
|
|
521
|
-
let workspaceEvidence;
|
|
522
|
-
if (existing) {
|
|
523
|
-
// A reconnect observes the existing input history. Only the caller's persisted
|
|
524
|
-
// never-submitted fact and verified empty-reference launch can authorize deferred first input.
|
|
525
|
-
if (input.initialPromptNotSubmitted !== true)
|
|
526
|
-
return { kind: "reconnected", observation: before, promptSubmission: "not-submitted", launchEvidence: null };
|
|
527
|
-
const evidence = input.launchEvidence;
|
|
528
|
-
const expectedArgv = [context.agent, ...args];
|
|
529
|
-
if (evidence === undefined || evidence.referenceBaseline !== "empty" || evidence.ownerLabel !== herdrExecutionLabel(context) || evidence.agent !== context.agent || !sameBinding(evidence.binding, binding) || !Array.isArray(evidence.argv) || evidence.argv.length !== expectedArgv.length || evidence.argv.some((value, index) => value !== expectedArgv[index]))
|
|
530
|
-
return rebind("unverified-launch");
|
|
531
|
-
if (!validWorkspaceEvidence(context, binding, evidence.workspaceEvidence) || (input.workspaceEvidence !== undefined && input.workspaceEvidence !== null && !validWorkspaceEvidence(context, binding, input.workspaceEvidence)))
|
|
532
|
-
return rebind("unverified-workspace-environment");
|
|
533
|
-
workspaceEvidence = evidence.workspaceEvidence;
|
|
534
|
-
launchEvidence = evidence;
|
|
535
|
-
}
|
|
536
|
-
else if (input.initialPromptNotSubmitted === true) {
|
|
537
|
-
return rebind("unverified-launch");
|
|
538
|
-
}
|
|
539
|
-
else if (before.nativeSession !== null) {
|
|
540
|
-
return rebind("retained-native-reference");
|
|
541
|
-
}
|
|
542
|
-
else {
|
|
543
|
-
if (!validWorkspaceEvidence(context, binding, input.workspaceEvidence))
|
|
544
|
-
return rebind("unverified-workspace-environment");
|
|
545
|
-
if (input.launchEvidence !== undefined && !validWorkspaceEvidence(context, binding, input.launchEvidence.workspaceEvidence))
|
|
546
|
-
return rebind("unverified-workspace-environment");
|
|
547
|
-
workspaceEvidence = input.workspaceEvidence;
|
|
548
|
-
}
|
|
549
|
-
await options.preflight.verifyStart({ context, configuration: input.configuration, ...(input.resumeSessionId === undefined ? {} : { resumeSessionId: input.resumeSessionId }) }, operation);
|
|
550
|
-
if (!existing) {
|
|
551
|
-
const timeout = Math.floor(Math.min(30_000, operation.remainingMs()));
|
|
552
|
-
if (timeout <= 3000)
|
|
553
|
-
throw new HerdrAgentActionError("The remaining operation budget cannot start a native agent.", "agent.start", "not-submitted", { promptSubmission: "not-submitted" });
|
|
554
|
-
nativeStartPossible = true;
|
|
555
|
-
const started = await mutation("agent.start", { name: herdrExecutionAgentName(context), kind: context.agent, pane_id: binding.paneId, args, timeout_ms: timeout }, "agent_started", operation);
|
|
556
|
-
const acknowledged = object(started.agent, "Native launch receipt");
|
|
557
|
-
const argv = array(started.argv, "Native launch arguments");
|
|
558
|
-
const expectedArgv = [context.agent, ...args];
|
|
559
|
-
if (acknowledged.pane_id !== binding.paneId || acknowledged.terminal_id !== binding.terminalId || acknowledged.workspace_id !== binding.workspaceId || argv.length !== expectedArgv.length || argv.some((value, index) => value !== expectedArgv[index]))
|
|
560
|
-
throw new Error("The native launch receipt does not preserve its original target and frozen arguments.");
|
|
561
|
-
launchEvidence = { binding: { ...binding }, ownerLabel: herdrExecutionLabel(context), agent: context.agent, argv: [...expectedArgv], referenceBaseline: "empty", workspaceEvidence };
|
|
562
|
-
}
|
|
563
|
-
while (true) {
|
|
564
|
-
const current = await observation(context, binding, operation);
|
|
565
|
-
if (current.status === "blocked")
|
|
566
|
-
return { kind: "waiting", observation: current, promptSubmission: "not-submitted", launchEvidence };
|
|
567
|
-
const ready = !current.launchPending && current.interactiveReady && (current.kind === context.agent || current.nativeSession?.agent === context.agent || current.name === herdrExecutionAgentName(context));
|
|
568
|
-
const originalSessionReady = input.resumeSessionId === undefined || current.nativeSession === null || current.nativeSession.sessionId === canonicalId(input.resumeSessionId);
|
|
569
|
-
if (input.resumeSessionId !== undefined && current.nativeSession !== null && !originalSessionReady)
|
|
570
|
-
throw new Error("The resumed agent reported a different native session.");
|
|
571
|
-
if (ready && originalSessionReady) {
|
|
572
|
-
// Native SessionStart can run on the first user turn, including resume.
|
|
573
|
-
// The verified explicit launch authorizes one input; its reported identity is checked afterward.
|
|
574
|
-
// https://github.com/openai/codex/blob/6aaac694803004a63c6f393d153c96d22521a6b9/codex-rs/core/src/session/turn.rs#L340-L342
|
|
575
|
-
await submit(context, binding, input.prompt, operation, true);
|
|
576
|
-
promptSubmission = "submitted";
|
|
577
|
-
while (true) {
|
|
578
|
-
const reported = await observation(context, binding, operation);
|
|
579
|
-
if (reported.nativeSession !== null) {
|
|
580
|
-
if (input.resumeSessionId !== undefined && reported.nativeSession.sessionId !== canonicalId(input.resumeSessionId))
|
|
581
|
-
throw new Error("The resumed native identity changed after prompt submission.");
|
|
582
|
-
return { kind: "started", observation: reported, promptSubmission: "submitted", launchEvidence };
|
|
583
|
-
}
|
|
584
|
-
if (reported.status === "blocked")
|
|
585
|
-
return { kind: "waiting", observation: reported, promptSubmission: "submitted", launchEvidence };
|
|
586
|
-
await wait(operation);
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
await wait(operation);
|
|
590
|
-
}
|
|
591
|
-
});
|
|
403
|
+
while (true) {
|
|
404
|
+
const current = await observation(context, binding, operation);
|
|
405
|
+
if (current.status === "blocked")
|
|
406
|
+
throw new HerdrAgentActionError("The native agent requires operator action before its first input.", "agent.prompt", "not-submitted", { promptSubmission: "not-submitted" });
|
|
407
|
+
if (input.resumeSessionId !== undefined && current.nativeSession !== null && current.nativeSession.sessionId !== canonicalId(input.resumeSessionId))
|
|
408
|
+
throw new HerdrAgentActionError("The resumed agent reported a different native session.", "agent.start", "unknown");
|
|
409
|
+
if (!current.launchPending && current.interactiveReady && (current.kind === context.agent || current.nativeSession?.agent === context.agent || current.name === herdrExecutionAgentName(context)))
|
|
410
|
+
break;
|
|
411
|
+
await wait(operation);
|
|
412
|
+
}
|
|
592
413
|
}
|
|
593
414
|
catch (cause) {
|
|
594
|
-
const evidence = launchEvidence === null ? {} : { launchEvidence };
|
|
595
415
|
if (cause instanceof HerdrAgentActionError)
|
|
596
|
-
throw
|
|
597
|
-
throw new HerdrAgentActionError(
|
|
598
|
-
}
|
|
599
|
-
},
|
|
600
|
-
submitPrompt: (context, binding, prompt, operation) => serialized(binding.paneId, operation, () => submit(context, binding, prompt, operation)),
|
|
601
|
-
captureStop: async (context, binding, operation) => {
|
|
602
|
-
const current = await observation(context, binding, operation);
|
|
603
|
-
const result = expectedResult(await options.rpc.request("pane.process_info", { pane_id: binding.paneId }, operation), "pane_process_info");
|
|
604
|
-
const info = object(result.process_info, "Pane process observation");
|
|
605
|
-
if (info.pane_id !== binding.paneId)
|
|
606
|
-
throw new Error("The process observation belongs to another pane.");
|
|
607
|
-
const shellPid = pid(info.shell_pid);
|
|
608
|
-
const processes = info.foreground_processes === undefined ? [] : array(info.foreground_processes, "Foreground processes");
|
|
609
|
-
const records = processes.map((value) => object(value, "Foreground process"));
|
|
610
|
-
const processIds = [...new Set(records.map((value) => pid(value.pid)).filter((value) => value !== shellPid))];
|
|
611
|
-
const keys = [...new Set(records.filter((value) => pid(value.pid) !== shellPid && nativeInteractiveProcessEvidence(value, context.agent)).map((value) => pid(value.pid)))];
|
|
612
|
-
if (processIds.length === 0)
|
|
613
|
-
throw new Error("The original native process identities are unavailable; pane absence or an empty process list cannot establish stopped confirmation.");
|
|
614
|
-
if (current.launchPending && (current.name !== herdrExecutionAgentName(context) || !records.some((value) => pid(value.pid) !== shellPid && nativeProcessEvidence(value, context.agent))))
|
|
615
|
-
throw new Error("The pending task launch has no concrete original native process evidence.");
|
|
616
|
-
return { binding, ownerLabel: herdrExecutionLabel(context), agent: context.agent, nativeSessionId: current.nativeSession?.sessionId ?? null, shellPid, processIds, nativeProcessIds: keys.length === 0 ? null : keys, observedAt: Date.now() };
|
|
617
|
-
},
|
|
618
|
-
requestGracefulStop: (context, binding, operation) => serialized(binding.paneId, operation, () => submit(context, binding, "/exit", operation)),
|
|
619
|
-
// This observes only the current shell. Historical never-submitted launch state
|
|
620
|
-
// remains a separate durable caller precondition for deriving agentStopped.
|
|
621
|
-
confirmEmptyPane: async (context, binding, operation) => {
|
|
622
|
-
await emptyOwnedPane(context, binding, operation, "unlaunched");
|
|
623
|
-
return { noNativeProcess: true };
|
|
624
|
-
},
|
|
625
|
-
confirmStopped: async (context, binding, evidence, operation) => {
|
|
626
|
-
verifyStopObservation(context, binding, evidence);
|
|
627
|
-
const current = await readOwned(context, binding, operation, true);
|
|
628
|
-
await absentProcesses(evidence, operation);
|
|
629
|
-
if (current.pane !== null) {
|
|
630
|
-
const result = expectedResult(await options.rpc.request("pane.process_info", { pane_id: binding.paneId }, operation), "pane_process_info");
|
|
631
|
-
const info = object(result.process_info, "Current pane process observation");
|
|
632
|
-
if (info.pane_id !== binding.paneId)
|
|
633
|
-
throw new Error("The current process observation belongs to another pane.");
|
|
634
|
-
const currentShellPid = pid(info.shell_pid);
|
|
635
|
-
if (currentShellPid !== evidence.shellPid && await isExecuting(evidence.shellPid))
|
|
636
|
-
throw new Error("The original task shell is still executing or its absence is unconfirmed.");
|
|
637
|
-
const currentProcesses = info.foreground_processes === undefined ? [] : array(info.foreground_processes, "Current foreground processes");
|
|
638
|
-
if (currentProcesses.some((value) => pid(object(value, "Current foreground process").pid) !== currentShellPid))
|
|
639
|
-
throw new Error("The original pane still hosts foreground work.");
|
|
416
|
+
throw cause;
|
|
417
|
+
throw new HerdrAgentActionError("The native process could not become ready before its first input.", "agent.prompt", "not-submitted", { cause, promptSubmission: "not-submitted" });
|
|
640
418
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
const data = await snapshot(operation);
|
|
646
|
-
requireOriginalResourceAbsent(data, originalBinding);
|
|
647
|
-
if (data.workspaces.some((workspace) => workspace.label === herdrExecutionLabel(context)))
|
|
648
|
-
throw new Error("The original task label identifies another resource.");
|
|
649
|
-
await originalProcessesAbsent(evidence, operation);
|
|
650
|
-
return { agentStopped: true };
|
|
651
|
-
},
|
|
652
|
-
confirmRestoredStop: async (context, originalBinding, restoredBinding, evidence, operation) => {
|
|
653
|
-
verifyOriginalStopScope(context, originalBinding, evidence);
|
|
654
|
-
if (originalBinding.session !== restoredBinding.session || originalBinding.workspaceId !== restoredBinding.workspaceId || originalBinding.paneId !== restoredBinding.paneId)
|
|
655
|
-
throw new Error("The restored shell does not have the original task workspace and pane association.");
|
|
656
|
-
const { current } = await emptyOwnedPane(context, restoredBinding, operation, "restored");
|
|
657
|
-
if (evidence.nativeSessionId !== null && current.nativeSession !== null && evidence.nativeSessionId !== current.nativeSession.sessionId)
|
|
658
|
-
throw new Error("The restored shell retains a different original native session.");
|
|
659
|
-
await originalProcessesAbsent(evidence, operation);
|
|
660
|
-
return { agentStopped: true, binding: { ...restoredBinding } };
|
|
661
|
-
},
|
|
662
|
-
confirmReplacedStop: async (context, originalBinding, replacementBinding, evidence, operation) => {
|
|
663
|
-
verifyOriginalStopScope(context, originalBinding, evidence);
|
|
664
|
-
if (replacementBinding.session !== originalBinding.session)
|
|
665
|
-
throw new Error("The replacement belongs to another Herdr session.");
|
|
666
|
-
const { data } = await emptyOwnedPane(context, replacementBinding, operation, "unlaunched");
|
|
667
|
-
requireOriginalResourceAbsent(data, originalBinding);
|
|
668
|
-
await originalProcessesAbsent(evidence, operation);
|
|
669
|
-
return { agentStopped: true, binding: { ...replacementBinding } };
|
|
670
|
-
},
|
|
671
|
-
forceStop: (context, binding, evidence, operation) => serialized(binding.paneId, operation, async () => {
|
|
672
|
-
verifyStopObservation(context, binding, evidence);
|
|
673
|
-
const { data, pane } = await readOwned(context, binding, operation, true);
|
|
674
|
-
if (pane !== null) {
|
|
419
|
+
await submit(context, binding, input.prompt, operation, true);
|
|
420
|
+
// SessionStart may be emitted on the first input rather than process launch.
|
|
421
|
+
// https://github.com/openai/codex/blob/6aaac694803004a63c6f393d153c96d22521a6b9/codex-rs/core/src/session/turn.rs#L340-L342
|
|
422
|
+
while (true) {
|
|
675
423
|
const current = await observation(context, binding, operation);
|
|
676
|
-
if (
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
if (processInfo.pane_id !== binding.paneId || pid(processInfo.shell_pid) !== evidence.shellPid)
|
|
681
|
-
throw new Error("The original task shell identity has changed.");
|
|
682
|
-
const foreground = processInfo.foreground_processes === undefined ? [] : array(processInfo.foreground_processes, "Current stop processes");
|
|
683
|
-
const currentPids = foreground.map((value) => pid(object(value, "Current stop process").pid)).filter((value) => value !== evidence.shellPid);
|
|
684
|
-
if (currentPids.length !== 0 && !currentPids.some((value) => evidence.processIds.includes(value)))
|
|
685
|
-
throw new Error("The task pane now hosts processes outside the original stop observation.");
|
|
686
|
-
const guardLabel = `daynight-stop:${binding.terminalId}`;
|
|
687
|
-
const guards = data.tabs.filter((tab) => tab.workspace_id === binding.workspaceId && tab.label === guardLabel);
|
|
688
|
-
if (guards.length > 1)
|
|
689
|
-
throw new Error("The task stop anchor is duplicated.");
|
|
690
|
-
if (guards.length === 0) {
|
|
691
|
-
const result = await mutation("tab.create", { workspace_id: binding.workspaceId, cwd: context.cwd, focus: false, label: guardLabel, env: { ...workspaceEnvironment } }, "tab_created", operation);
|
|
692
|
-
const tab = object(result.tab, "Created stop anchor tab");
|
|
693
|
-
const root = object(result.root_pane, "Created stop anchor pane");
|
|
694
|
-
if (tab.workspace_id !== binding.workspaceId || tab.label !== guardLabel || root.workspace_id !== binding.workspaceId || root.pane_id === binding.paneId || root.agent !== undefined)
|
|
695
|
-
throw new HerdrAgentActionError("The stop anchor does not identify an empty task-owned pane.", "tab.create", "unknown");
|
|
424
|
+
if (current.nativeSession !== null) {
|
|
425
|
+
if (input.resumeSessionId !== undefined && current.nativeSession.sessionId !== canonicalId(input.resumeSessionId))
|
|
426
|
+
throw new Error("The resumed native identity differs from the requested session.");
|
|
427
|
+
return current;
|
|
696
428
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
throw new Error("The task workspace has no verified stop anchor.");
|
|
701
|
-
await mutation("pane.close", { pane_id: binding.paneId }, "ok", operation);
|
|
429
|
+
if (current.status === "blocked")
|
|
430
|
+
return current;
|
|
431
|
+
await wait(operation);
|
|
702
432
|
}
|
|
703
|
-
await readOwned(context, binding, operation, true);
|
|
704
|
-
await absentProcesses(evidence, operation);
|
|
705
|
-
return { agentStopped: true, originalPaneClosed: true };
|
|
706
433
|
}),
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
434
|
+
submitPrompt: (context, binding, prompt, operation) => serialized(binding.workspaceId, operation, () => submit(context, binding, prompt, operation)),
|
|
435
|
+
stopAndClose: (context, binding, operation) => serialized(binding.workspaceId, operation, async () => {
|
|
436
|
+
if (binding.session !== session)
|
|
437
|
+
throw new Error("The task binding belongs to another Herdr session.");
|
|
710
438
|
const before = await snapshot(operation);
|
|
711
|
-
const
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
439
|
+
const label = herdrExecutionLabel(context);
|
|
440
|
+
const key = JSON.stringify([binding.session, binding.workspaceId, label]);
|
|
441
|
+
const matching = before.workspaces.filter((workspace) => workspace.workspace_id === binding.workspaceId || workspace.label === label);
|
|
442
|
+
if (matching.length === 0) {
|
|
443
|
+
if ([...before.panes, ...before.agents].some((resource) => resource.workspace_id === binding.workspaceId || resource.pane_id === binding.paneId || resource.terminal_id === binding.terminalId))
|
|
444
|
+
throw new Error("The missing task workspace still has associated resources.");
|
|
445
|
+
return finishStop(key, operation);
|
|
446
|
+
}
|
|
447
|
+
if (matching.length !== 1 || matching[0].workspace_id !== binding.workspaceId || matching[0].label !== label)
|
|
448
|
+
throw new Error("The task workspace identity changed before stopping.");
|
|
449
|
+
const currentPanes = before.panes.filter((pane) => pane.workspace_id === binding.workspaceId);
|
|
450
|
+
// Closing removes discovery evidence before all processes necessarily exit.
|
|
451
|
+
// Retain this process-local responsibility across failed close/wait calls.
|
|
452
|
+
let pending = stoppingProcesses.get(key);
|
|
453
|
+
if (pending === undefined) {
|
|
454
|
+
pending = { context: { ...context }, binding: { ...binding }, processIds: new Set() };
|
|
455
|
+
stoppingProcesses.set(key, pending);
|
|
456
|
+
}
|
|
457
|
+
for (const pane of currentPanes) {
|
|
458
|
+
const paneId = text(pane.pane_id, "Stop pane ID");
|
|
459
|
+
if (paneId === binding.paneId && pane.terminal_id !== binding.terminalId)
|
|
460
|
+
throw new Error("The task terminal identity changed before stopping.");
|
|
461
|
+
await verifyCwd(pane.cwd, context);
|
|
462
|
+
const result = expectedResult(await options.rpc.request("pane.process_info", { pane_id: paneId }, operation), "pane_process_info");
|
|
463
|
+
const info = object(result.process_info, "Current stop process observation");
|
|
464
|
+
if (info.pane_id !== paneId)
|
|
465
|
+
throw new Error("The stop process observation belongs to another pane.");
|
|
466
|
+
pending.processIds.add(pid(info.shell_pid));
|
|
467
|
+
const foreground = info.foreground_processes === undefined ? [] : array(info.foreground_processes, "Current foreground processes");
|
|
468
|
+
for (const entry of foreground)
|
|
469
|
+
pending.processIds.add(pid(object(entry, "Current foreground process").pid));
|
|
716
470
|
}
|
|
717
|
-
await readOwned(context, binding, operation, true);
|
|
718
471
|
await mutation("workspace.close", { workspace_id: binding.workspaceId, close_group: false }, "ok", operation);
|
|
719
472
|
const after = await snapshot(operation);
|
|
720
|
-
if (after.workspaces.some((workspace) => workspace.workspace_id === binding.workspaceId || workspace.label ===
|
|
721
|
-
throw new HerdrAgentActionError("The
|
|
722
|
-
|
|
723
|
-
},
|
|
473
|
+
if (after.workspaces.some((workspace) => workspace.workspace_id === binding.workspaceId || workspace.label === label) || [...after.panes, ...after.agents].some((resource) => resource.workspace_id === binding.workspaceId))
|
|
474
|
+
throw new HerdrAgentActionError("The task workspace still exists after closure.", "workspace.close", "unknown");
|
|
475
|
+
await finishStop(key, operation);
|
|
476
|
+
}),
|
|
724
477
|
};
|
|
725
478
|
};
|
|
726
479
|
//# sourceMappingURL=agents.js.map
|