@ours.network/fleet 2.3.3 → 2.3.5
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/application/task-room-service.d.ts +8 -0
- package/dist/application/task-room-service.js +15 -0
- package/dist/build-info.json +4 -4
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +22 -0
- package/dist/ops.js +6 -0
- package/dist/rooms-tasks/cli.js +24 -0
- package/dist/rooms-tasks/deletion.js +4 -0
- package/dist/rooms-tasks/task-review.js +2 -0
- package/dist/rooms-tasks/task-state.d.ts +6 -1
- package/dist/rooms-tasks/task-state.js +20 -1
- package/dist/rooms-tasks/types.d.ts +17 -0
- package/dist/rooms-tasks/workspace-removal.d.ts +85 -0
- package/dist/rooms-tasks/workspace-removal.js +633 -0
- package/dist/rooms-tasks/workspace.d.ts +9 -1
- package/dist/rooms-tasks/workspace.js +13 -1
- package/dist/spawn.js +2 -0
- package/dist/web/server.js +10 -1
- package/docs/task-workspaces.md +51 -1
- package/package.json +2 -2
package/dist/spawn.js
CHANGED
|
@@ -2,6 +2,7 @@ import { taskSupervisorMayRun } from './task-supervision.js';
|
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import { storeRoomSecret, storeTemporaryLaunch, preparePermanentAssignment } from './agent-ours/service.js';
|
|
4
4
|
import { ensureWorkspace } from './rooms-tasks/workspace.js';
|
|
5
|
+
import { assertCwdNotInRemovedTaskFolder } from './rooms-tasks/workspace-removal.js';
|
|
5
6
|
import { spawn as spawnChild } from 'node:child_process';
|
|
6
7
|
import { closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
7
8
|
import { join } from 'node:path';
|
|
@@ -324,6 +325,7 @@ export async function spawnTemp(o, binPath, launch = independentSupervisor, crea
|
|
|
324
325
|
if (o.isolationFile)
|
|
325
326
|
readIsolationFile(o.isolationFile); // fail before reserving
|
|
326
327
|
const prepared = resolvedSpawn(o); // canonical validation before mutation
|
|
328
|
+
assertCwdNotInRemovedTaskFolder(prepared.role.cwd);
|
|
327
329
|
if ((o.taskOwner || o.roomMemberStartup?.task_id) && !getAdapter(prepared.role.harness).supportsResume)
|
|
328
330
|
throw Error('TASK_HARNESS_RESUME_UNSUPPORTED: task agents require a resumable harness');
|
|
329
331
|
if (o.roomMemberStartup?.workspace) {
|
package/dist/web/server.js
CHANGED
|
@@ -23,6 +23,7 @@ import { AuditSink } from './audit.js';
|
|
|
23
23
|
import { WebAuth } from './auth.js';
|
|
24
24
|
import { FleetEventBus } from './events.js';
|
|
25
25
|
import { ROLE_NAME_RE } from '../config.js';
|
|
26
|
+
import { workingFilesSummary } from '../rooms-tasks/workspace-removal.js';
|
|
26
27
|
import { TaskListError } from '../rooms-tasks/task-lists.js';
|
|
27
28
|
import { acknowledgeTaskProgress, acknowledgeTaskReview, contributionUnread, resolveTaskRef, TaskStateError } from '../rooms-tasks/task-state.js';
|
|
28
29
|
import { CONTRIBUTION_CUTOFF_MAX, CONTRIBUTION_PAGE_MAX, listTaskContributions, readTaskContributions } from '../rooms-tasks/task-contributions.js';
|
|
@@ -390,7 +391,7 @@ export async function buildWebServer(services, boundary, options = {}) {
|
|
|
390
391
|
/** Browser task payloads carry Progress/To review counts; bodies have their own routes. */
|
|
391
392
|
const wireTask = (task) => {
|
|
392
393
|
const { progress: _progress, review: _review, member_naming: _naming, ...rest } = task;
|
|
393
|
-
return { ...rest, ...taskContributionSummary(task) };
|
|
394
|
+
return { ...rest, ...taskContributionSummary(task), working_files: workingFilesSummary(task) };
|
|
394
395
|
};
|
|
395
396
|
const wireTaskResult = (result) => {
|
|
396
397
|
const value = result;
|
|
@@ -625,6 +626,14 @@ export async function buildWebServer(services, boundary, options = {}) {
|
|
|
625
626
|
recovery: `Repeat DELETE /api/v1/tasks/${submitted}?confirm=${submitted}.`,
|
|
626
627
|
};
|
|
627
628
|
});
|
|
629
|
+
// Removes only the Done task's own working folder; the task, Progress, To review files and chat history stay.
|
|
630
|
+
app.post('/api/v1/tasks/:id/working-files/remove', async (request) => {
|
|
631
|
+
auth.authenticate(request, true);
|
|
632
|
+
const result = await taskApi(() => requireTaskRooms().removeTaskWorkingFiles({
|
|
633
|
+
actor: { kind: 'local_control', surface: 'web' }, taskId: taskRef(request),
|
|
634
|
+
}));
|
|
635
|
+
return { task: wireTask(result.task), result: result.result, mentions: result.mentions };
|
|
636
|
+
});
|
|
628
637
|
app.post('/api/v1/tasks', async (request, reply) => {
|
|
629
638
|
auth.authenticate(request, true);
|
|
630
639
|
const body = request.body;
|
package/docs/task-workspaces.md
CHANGED
|
@@ -30,6 +30,7 @@ complete internal worktree registries, including dirty and stale owned entries.
|
|
|
30
30
|
| Operation | Workspace and execution artifacts | Member runtime evidence |
|
|
31
31
|
| --- | --- | --- |
|
|
32
32
|
| Complete, fail, cancel | Retained while Task is recorded | Retained in Fleet recovery archives |
|
|
33
|
+
| Explicit working-files removal (Done Task only) | Task workspace removed; Task, Progress and To review files retained | Retained in Fleet recovery archives |
|
|
33
34
|
| Linked Room retirement or deletion | Task workspace retained | Retained |
|
|
34
35
|
| Internal standalone Room retirement | Room workspace retained | Retained |
|
|
35
36
|
| Explicit Task deletion | Removed after member retirement, with the task's retained To review files | All proven owned retired launches removed |
|
|
@@ -39,7 +40,8 @@ Internal room close means retirement, not destructive Task deletion.
|
|
|
39
40
|
The existing CLI `room close ID ID` remains a deprecated alias for explicit
|
|
40
41
|
`room delete ID ID`: it deletes standalone room-owned artifacts, but always
|
|
41
42
|
retains a linked Task workspace. Its repeated-ID confirmation is unchanged. To remove a Task's
|
|
42
|
-
artifacts, explicitly delete the Task
|
|
43
|
+
artifacts, explicitly delete the Task, or, for a Done Task, remove only its working files
|
|
44
|
+
(`task remove-files ID`, below). Cancellation is not deletion. Automatic
|
|
43
45
|
compatibility cleanup of legacy closed Rooms skips records with owned workspaces.
|
|
44
46
|
These choices reconcile close/remove wording with the requirement that completed,
|
|
45
47
|
recorded Tasks retain all artifacts.
|
|
@@ -223,3 +225,51 @@ requires a supported Home Screen installation and granted device/browser permiss
|
|
|
223
225
|
reopen the workspace to reconcile an expired subscription and explicitly re-enable
|
|
224
226
|
permission if it was revoked. Every received push must display a notification to
|
|
225
227
|
honor Safari's user-visible push requirement.
|
|
228
|
+
|
|
229
|
+
## Removing a Done Task's working files
|
|
230
|
+
|
|
231
|
+
`ours-fleet task remove-files ID` (API `POST /api/v1/tasks/ID/working-files/remove`)
|
|
232
|
+
frees disk space without deleting the Task. It removes only the recorded owned
|
|
233
|
+
workspace through the same guarded deletion as Task deletion (marker, Git
|
|
234
|
+
registration, symlink and mount checks, token tombstone). It never collects or
|
|
235
|
+
erases recovery archives, member artifacts or the To review store, and never
|
|
236
|
+
closes rooms or stops agents. It runs under the task operation lock and refuses
|
|
237
|
+
with the reason unless all of these hold:
|
|
238
|
+
|
|
239
|
+
- the Task is Done with its completion settled, and is not being deleted;
|
|
240
|
+
- no room record names the Task or its workspace, and every room record is readable;
|
|
241
|
+
- a layout run exists, is closed, is not uncertain, and has retired every owned participant;
|
|
242
|
+
- no supervisor registration belongs to the Task;
|
|
243
|
+
- no unarchived temporary agent state belongs to the Task or works in the
|
|
244
|
+
folder (cwd or startup workspace, literally or through symlinks), and all
|
|
245
|
+
such state is readable;
|
|
246
|
+
- no permanent agent's recorded `.cwd` or configured cwd is in the folder.
|
|
247
|
+
|
|
248
|
+
The intent is recorded before a final in-use scan. Agent state that names a
|
|
249
|
+
task folder is created under that folder's use lock and refuses a folder
|
|
250
|
+
whose removal began, so a concurrent launch is either seen by the scan or
|
|
251
|
+
refused. After the folder is renamed to its tombstone and before anything
|
|
252
|
+
is removed, every process's cwd, root and open files are checked (Linux
|
|
253
|
+
`/proc`). A process that entered the folder earlier restores the folder and
|
|
254
|
+
refuses the removal. Platforms without this check refuse the removal.
|
|
255
|
+
A same-user process whose cwd and descriptors are unreadable (for example
|
|
256
|
+
non-dumpable, or in a container) refuses the removal unless it is a zombie.
|
|
257
|
+
Nothing readable proves such a process holds nothing in the folder: a
|
|
258
|
+
retained cwd or descriptor survives namespace and chroot changes, and a user
|
|
259
|
+
service manager may keep descriptors in its store. The refusal names each
|
|
260
|
+
such process by pid and kernel name (never its command line), with
|
|
261
|
+
read-only context such as "own mount namespace without the folder".
|
|
262
|
+
Another user's unreadable process is skipped when a private ancestor closes
|
|
263
|
+
the folder to it. Superuser processes are outside the check. On hosts that
|
|
264
|
+
run rootless containers or a user service manager, this strict check can
|
|
265
|
+
make removal unavailable while those processes run. Only a folder
|
|
266
|
+
that this run renamed intact is restored. A partly removed tombstone from an
|
|
267
|
+
earlier run stays in place with the refusal recorded, so a later retry can
|
|
268
|
+
finish it.
|
|
269
|
+
|
|
270
|
+
The Task records `workspace_removal` (started, completed, error), shown as
|
|
271
|
+
`working_files` in API payloads. A retry resumes an unfinished tombstone, and
|
|
272
|
+
repeating a finished removal changes nothing. A folder already missing on the
|
|
273
|
+
first run is recorded as such. A create replay never recreates the folder, and
|
|
274
|
+
To review files can no longer be attached from it. Explicit Task deletion
|
|
275
|
+
still works and first finishes an interrupted removal.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ours.network/fleet",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.5",
|
|
4
4
|
"description": "Harness-agnostic fleet of persistent, identity-bound AI agents. Declarative fleet.yaml, managed native/ACP sessions, supervision, and ours.network messaging.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "FSL-1.1-Apache-2.0",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"@agentclientprotocol/sdk": "^1.3.0",
|
|
51
51
|
"@fastify/websocket": "^11.2.0",
|
|
52
52
|
"@ours.network/cli": "4.2.0",
|
|
53
|
-
"@ours.network/mcp": "2.0.
|
|
53
|
+
"@ours.network/mcp": "2.0.4",
|
|
54
54
|
"@ours.network/sdk": "5.2.0",
|
|
55
55
|
"commander": "^12.1.0",
|
|
56
56
|
"fastify": "^5.4.0",
|