@akira-tl/forgerelay 0.8.2 → 0.8.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 +16 -0
- package/capabilities/workspace-tasks/GUIDE.md +50 -0
- package/dist/capabilities.js +6 -0
- package/dist/capability-registry.js +96 -21
- package/dist/config.js +11 -0
- package/dist/remote-workspace-relay.js +15 -0
- package/dist/server.js +449 -34
- package/dist/subagents/sessions/capability.js +3 -0
- package/dist/workspace-task-reminders.js +42 -0
- package/dist/workspace-tasks.js +420 -0
- package/dist/workspaces.js +62 -30
- package/docs/chatgpt-coding-workflow.md +13 -3
- package/docs/configuration.md +46 -11
- package/package.json +2 -2
- package/scripts/debug/accept.mjs +298 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akira-tl/forgerelay",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.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/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",
|
|
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/workspace-tasks.test.ts && tsx src/workspace-task-reminders.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",
|
package/scripts/debug/accept.mjs
CHANGED
|
@@ -48,6 +48,10 @@ mkdirSync(acceptanceRoot, { recursive: true });
|
|
|
48
48
|
setupGitProject(checkoutWorkspace);
|
|
49
49
|
setupGitProject(lifecycleDeleteWorkspace);
|
|
50
50
|
writeFileSync(join(lifecycleDeleteWorkspace, "keep.txt"), "keep checkout files\n");
|
|
51
|
+
writeFileSync(
|
|
52
|
+
join(lifecycleDeleteWorkspace, "AGENTS.md"),
|
|
53
|
+
"INSPECTION_ACCEPTANCE_BOOTSTRAP_SECRET\n",
|
|
54
|
+
);
|
|
51
55
|
setupCodeIntelligenceAcceptanceProject({
|
|
52
56
|
root: checkoutWorkspace,
|
|
53
57
|
fakeLanguageServer,
|
|
@@ -265,6 +269,7 @@ try {
|
|
|
265
269
|
"hooks.lifecycle",
|
|
266
270
|
"capability-guides.read",
|
|
267
271
|
"code.intelligence",
|
|
272
|
+
"workspace.tasks",
|
|
268
273
|
"batch.execute",
|
|
269
274
|
...(process.platform === "linux" ? ["artifact.native-download"] : []),
|
|
270
275
|
"ui.mcp-app",
|
|
@@ -276,12 +281,116 @@ try {
|
|
|
276
281
|
"hooks.check",
|
|
277
282
|
"review.changes",
|
|
278
283
|
"code.intelligence",
|
|
284
|
+
"workspace.tasks",
|
|
279
285
|
"batch.execute",
|
|
280
286
|
...(process.platform === "linux" ? ["artifact.download"] : []),
|
|
281
287
|
]);
|
|
282
288
|
assert.equal(capabilityCatalog[0].available, true);
|
|
283
289
|
assert.equal(capabilityCatalog[0].guide.name, "lifecycle-hooks");
|
|
284
290
|
|
|
291
|
+
const checkoutTaskStatePath = join(stateDir, "workspaces", workspaceId, "tasks.json");
|
|
292
|
+
assert.ok(existsSync(checkoutTaskStatePath));
|
|
293
|
+
const checkoutTaskList = callTool(oauth.accessToken, sessionId, 130, "capability", {
|
|
294
|
+
workspaceId,
|
|
295
|
+
name: "workspace.tasks",
|
|
296
|
+
action: "run",
|
|
297
|
+
arguments: { operation: "list.create", name: "7677 release tasks" },
|
|
298
|
+
});
|
|
299
|
+
assert.equal(checkoutTaskList.isError, undefined);
|
|
300
|
+
const checkoutListId = checkoutTaskList.structuredContent.result.lists[0].id;
|
|
301
|
+
const checkoutTask = callTool(oauth.accessToken, sessionId, 131, "capability", {
|
|
302
|
+
workspaceId,
|
|
303
|
+
name: "workspace.tasks",
|
|
304
|
+
action: "run",
|
|
305
|
+
arguments: {
|
|
306
|
+
operation: "task.create",
|
|
307
|
+
listId: checkoutListId,
|
|
308
|
+
subject: "Verify v0.8.3 Task persistence",
|
|
309
|
+
content: "created through real MCP",
|
|
310
|
+
status: "in_progress",
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
assert.equal(checkoutTask.isError, undefined);
|
|
314
|
+
const checkoutTaskId = checkoutTask.structuredContent.result.lists[0].tasks[0].id;
|
|
315
|
+
const taskFingerprintBeforeExternal = checkoutTask.structuredContent.result.fingerprint;
|
|
316
|
+
const externalTaskState = JSON.parse(readFileSync(checkoutTaskStatePath, "utf8"));
|
|
317
|
+
externalTaskState.lists[0].tasks[0].content = "reloaded external task edit";
|
|
318
|
+
writeFileSync(checkoutTaskStatePath, `${JSON.stringify(externalTaskState, null, 2)}\n`);
|
|
319
|
+
const reloadedCheckoutTasks = callTool(oauth.accessToken, sessionId, 132, "capability", {
|
|
320
|
+
workspaceId,
|
|
321
|
+
name: "workspace.tasks",
|
|
322
|
+
action: "run",
|
|
323
|
+
arguments: {
|
|
324
|
+
operation: "get",
|
|
325
|
+
level: "detail",
|
|
326
|
+
listId: checkoutListId,
|
|
327
|
+
taskId: checkoutTaskId,
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
assert.equal(reloadedCheckoutTasks.isError, undefined);
|
|
331
|
+
assert.equal(reloadedCheckoutTasks.structuredContent.result.task.id, checkoutTaskId);
|
|
332
|
+
assert.equal(reloadedCheckoutTasks.structuredContent.result.task.content, "reloaded external task edit");
|
|
333
|
+
assert.notEqual(reloadedCheckoutTasks.structuredContent.result.fingerprint, taskFingerprintBeforeExternal);
|
|
334
|
+
|
|
335
|
+
const checkoutTaskSummary = callTool(oauth.accessToken, sessionId, 150, "capability", {
|
|
336
|
+
workspaceId,
|
|
337
|
+
name: "workspace.tasks",
|
|
338
|
+
action: "run",
|
|
339
|
+
arguments: { operation: "get" },
|
|
340
|
+
});
|
|
341
|
+
const summaryProjection = checkoutTaskSummary.structuredContent.result;
|
|
342
|
+
assert.equal(summaryProjection.level, "summary");
|
|
343
|
+
assert.equal(summaryProjection.lists[0].id, checkoutListId);
|
|
344
|
+
assert.equal(summaryProjection.lists[0].taskCount, 1);
|
|
345
|
+
assert.equal(summaryProjection.lists[0].unfinishedTaskCount, 1);
|
|
346
|
+
assert.equal(JSON.stringify(summaryProjection).includes(checkoutTaskId), false);
|
|
347
|
+
assert.equal(JSON.stringify(summaryProjection).includes("reloaded external task edit"), false);
|
|
348
|
+
|
|
349
|
+
const checkoutTaskHeaders = callTool(oauth.accessToken, sessionId, 151, "capability", {
|
|
350
|
+
workspaceId,
|
|
351
|
+
name: "workspace.tasks",
|
|
352
|
+
action: "run",
|
|
353
|
+
arguments: { operation: "get", level: "headers", listId: checkoutListId },
|
|
354
|
+
});
|
|
355
|
+
const headersProjection = checkoutTaskHeaders.structuredContent.result;
|
|
356
|
+
assert.equal(headersProjection.level, "headers");
|
|
357
|
+
assert.equal(headersProjection.lists.length, 1);
|
|
358
|
+
assert.equal(headersProjection.lists[0].tasks[0].id, checkoutTaskId);
|
|
359
|
+
assert.equal(headersProjection.lists[0].tasks[0].subject, "Verify v0.8.3 Task persistence");
|
|
360
|
+
assert.equal("content" in headersProjection.lists[0].tasks[0], false);
|
|
361
|
+
|
|
362
|
+
const checkoutTaskDetail = callTool(oauth.accessToken, sessionId, 152, "capability", {
|
|
363
|
+
workspaceId,
|
|
364
|
+
name: "workspace.tasks",
|
|
365
|
+
action: "run",
|
|
366
|
+
arguments: {
|
|
367
|
+
operation: "get",
|
|
368
|
+
level: "detail",
|
|
369
|
+
listId: checkoutListId,
|
|
370
|
+
taskId: checkoutTaskId,
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
const detailProjection = checkoutTaskDetail.structuredContent.result;
|
|
374
|
+
assert.equal(detailProjection.level, "detail");
|
|
375
|
+
assert.equal(detailProjection.task.id, checkoutTaskId);
|
|
376
|
+
assert.equal(detailProjection.task.content, "reloaded external task edit");
|
|
377
|
+
pass("workspace.tasks progressive disclosure", "summary -> headers -> one Task detail through real MCP");
|
|
378
|
+
|
|
379
|
+
for (let index = 0; index < 29; index += 1) {
|
|
380
|
+
const semanticWork = callTool(oauth.accessToken, sessionId, 160 + index, "read", {
|
|
381
|
+
workspaceId,
|
|
382
|
+
path: "README.md",
|
|
383
|
+
});
|
|
384
|
+
assert.doesNotMatch(toolText(semanticWork), /Reminder: this Workspace has unfinished active Tasks/);
|
|
385
|
+
}
|
|
386
|
+
const reminderWork = callTool(oauth.accessToken, sessionId, 189, "read", {
|
|
387
|
+
workspaceId,
|
|
388
|
+
path: "README.md",
|
|
389
|
+
});
|
|
390
|
+
assert.match(toolText(reminderWork), /Reminder: this Workspace has unfinished active Tasks/);
|
|
391
|
+
assert.equal(toolText(reminderWork).includes("reloaded external task edit"), false);
|
|
392
|
+
pass("workspace.tasks reminder", "default 30 semantic work calls emitted one body-free reminder");
|
|
393
|
+
|
|
285
394
|
const repeatedOpen = callTool(oauth.accessToken, sessionId, 84, "open_workspace", {
|
|
286
395
|
path: checkoutWorkspace,
|
|
287
396
|
newWorkspace: true,
|
|
@@ -312,6 +421,13 @@ try {
|
|
|
312
421
|
assert.equal(closedWorkspace.isError, undefined);
|
|
313
422
|
assert.equal(closedWorkspace.structuredContent.workspaceId, workspaceId);
|
|
314
423
|
assert.equal(closedWorkspace.structuredContent.action, "close");
|
|
424
|
+
const closedCheckoutTasks = callTool(oauth.accessToken, sessionId, 133, "capability", {
|
|
425
|
+
workspaceId,
|
|
426
|
+
name: "workspace.tasks",
|
|
427
|
+
action: "run",
|
|
428
|
+
arguments: { operation: "get" },
|
|
429
|
+
});
|
|
430
|
+
assert.equal(closedCheckoutTasks.isError, true);
|
|
315
431
|
|
|
316
432
|
const closedInventory = callTool(oauth.accessToken, sessionId, 87, "open_workspace", {
|
|
317
433
|
action: "list",
|
|
@@ -336,12 +452,101 @@ try {
|
|
|
336
452
|
resumedOriginal.structuredContent.contextFingerprint,
|
|
337
453
|
opened.structuredContent.contextFingerprint,
|
|
338
454
|
);
|
|
455
|
+
const resumedCheckoutTasks = callTool(oauth.accessToken, sessionId, 134, "capability", {
|
|
456
|
+
workspaceId,
|
|
457
|
+
name: "workspace.tasks",
|
|
458
|
+
action: "run",
|
|
459
|
+
arguments: {
|
|
460
|
+
operation: "get",
|
|
461
|
+
level: "detail",
|
|
462
|
+
listId: checkoutListId,
|
|
463
|
+
taskId: checkoutTaskId,
|
|
464
|
+
},
|
|
465
|
+
});
|
|
466
|
+
assert.equal(resumedCheckoutTasks.isError, undefined);
|
|
467
|
+
assert.equal(resumedCheckoutTasks.structuredContent.result.task.id, checkoutTaskId);
|
|
468
|
+
assert.equal(resumedCheckoutTasks.structuredContent.result.task.content, "reloaded external task edit");
|
|
469
|
+
assert.equal(resumedOriginal.structuredContent.contextFingerprint, opened.structuredContent.contextFingerprint);
|
|
339
470
|
|
|
340
471
|
const deleteOpened = callTool(oauth.accessToken, sessionId, 91, "open_workspace", {
|
|
341
472
|
path: lifecycleDeleteWorkspace,
|
|
342
473
|
context: "none",
|
|
343
474
|
}, { "openai/session": "acceptance-workspace-delete" });
|
|
344
475
|
const deleteWorkspaceId = deleteOpened.structuredContent.workspaceId;
|
|
476
|
+
const deleteTaskStatePath = join(stateDir, "workspaces", deleteWorkspaceId, "tasks.json");
|
|
477
|
+
const deleteTaskList = callTool(oauth.accessToken, sessionId, 135, "capability", {
|
|
478
|
+
workspaceId: deleteWorkspaceId,
|
|
479
|
+
name: "workspace.tasks",
|
|
480
|
+
action: "run",
|
|
481
|
+
arguments: { operation: "list.create", name: "delete with workspace" },
|
|
482
|
+
});
|
|
483
|
+
assert.equal(deleteTaskList.isError, undefined);
|
|
484
|
+
const deleteTaskListId = deleteTaskList.structuredContent.result.lists[0].id;
|
|
485
|
+
const inspectionTask = callTool(oauth.accessToken, sessionId, 190, "capability", {
|
|
486
|
+
workspaceId: deleteWorkspaceId,
|
|
487
|
+
name: "workspace.tasks",
|
|
488
|
+
action: "run",
|
|
489
|
+
arguments: {
|
|
490
|
+
operation: "task.create",
|
|
491
|
+
listId: deleteTaskListId,
|
|
492
|
+
subject: "Inspect safely",
|
|
493
|
+
content: "INSPECTION_ACCEPTANCE_TASK_BODY_SECRET",
|
|
494
|
+
status: "in_progress",
|
|
495
|
+
},
|
|
496
|
+
});
|
|
497
|
+
assert.equal(inspectionTask.isError, undefined);
|
|
498
|
+
assert.ok(existsSync(deleteTaskStatePath));
|
|
499
|
+
|
|
500
|
+
const inspectionBefore = callTool(oauth.accessToken, sessionId, 191, "open_workspace", {
|
|
501
|
+
action: "list",
|
|
502
|
+
workspaceId: deleteWorkspaceId,
|
|
503
|
+
}, workspaceConversationMeta);
|
|
504
|
+
const inspectionBeforeEntry = inspectionBefore.structuredContent.workspaces[0];
|
|
505
|
+
assert.equal(inspectionBeforeEntry.current, false);
|
|
506
|
+
const inspectedWorkspace = callTool(oauth.accessToken, sessionId, 192, "open_workspace", {
|
|
507
|
+
action: "inspect",
|
|
508
|
+
workspaceId: deleteWorkspaceId,
|
|
509
|
+
}, workspaceConversationMeta);
|
|
510
|
+
assert.equal(inspectedWorkspace.structuredContent.action, "inspect");
|
|
511
|
+
const inspectionProjection = inspectedWorkspace.structuredContent.inspection;
|
|
512
|
+
assert.equal(inspectionProjection.workspaceId, deleteWorkspaceId);
|
|
513
|
+
assert.equal(inspectionProjection.kind, "workspace");
|
|
514
|
+
assert.equal(inspectionProjection.location, "local");
|
|
515
|
+
assert.equal(inspectionProjection.root, lifecycleDeleteWorkspace);
|
|
516
|
+
assert.equal(inspectionProjection.taskSummary.level, "summary");
|
|
517
|
+
assert.equal(inspectionProjection.taskSummary.lists[0].taskCount, 1);
|
|
518
|
+
assert.equal(inspectionProjection.taskSummary.lists[0].unfinishedTaskCount, 1);
|
|
519
|
+
const inspectionJson = JSON.stringify(inspectedWorkspace);
|
|
520
|
+
for (const forbidden of [
|
|
521
|
+
"INSPECTION_ACCEPTANCE_BOOTSTRAP_SECRET",
|
|
522
|
+
"INSPECTION_ACCEPTANCE_TASK_BODY_SECRET",
|
|
523
|
+
"\"fingerprint\"",
|
|
524
|
+
"\"agentsFiles\"",
|
|
525
|
+
"\"availableAgentsFiles\"",
|
|
526
|
+
"\"capabilityGuides\"",
|
|
527
|
+
"\"skillDiagnostics\"",
|
|
528
|
+
"\"agentProviders\"",
|
|
529
|
+
"\"contextFingerprint\"",
|
|
530
|
+
"\"capabilityFingerprint\"",
|
|
531
|
+
"\"memberContext\"",
|
|
532
|
+
]) {
|
|
533
|
+
assert.equal(inspectionJson.includes(forbidden), false, `Workspace inspect leaked ${forbidden}`);
|
|
534
|
+
}
|
|
535
|
+
const inspectionAfter = callTool(oauth.accessToken, sessionId, 193, "open_workspace", {
|
|
536
|
+
action: "list",
|
|
537
|
+
workspaceId: deleteWorkspaceId,
|
|
538
|
+
}, workspaceConversationMeta);
|
|
539
|
+
assert.equal(inspectionAfter.structuredContent.workspaces[0].lastUsedAt, inspectionBeforeEntry.lastUsedAt);
|
|
540
|
+
assert.equal(inspectionAfter.structuredContent.workspaces[0].current, false);
|
|
541
|
+
const callerAfterInspection = callTool(oauth.accessToken, sessionId, 194, "open_workspace", {
|
|
542
|
+
workspaceId,
|
|
543
|
+
context: "auto",
|
|
544
|
+
}, workspaceConversationMeta);
|
|
545
|
+
assert.equal(callerAfterInspection.structuredContent.workspaceId, workspaceId);
|
|
546
|
+
assert.equal(callerAfterInspection.structuredContent.agentsFiles, undefined);
|
|
547
|
+
assert.equal(callerAfterInspection.structuredContent.capabilityGuides, undefined);
|
|
548
|
+
pass("Workspace inspection", "cross-Workspace metadata + Task summary stayed read-only and bootstrap-free");
|
|
549
|
+
|
|
345
550
|
const deleteClosed = callTool(oauth.accessToken, sessionId, 92, "close_workspace", {
|
|
346
551
|
workspaceId: deleteWorkspaceId,
|
|
347
552
|
});
|
|
@@ -353,12 +558,17 @@ try {
|
|
|
353
558
|
assert.equal(deletedWorkspace.isError, undefined);
|
|
354
559
|
assert.equal(deletedWorkspace.structuredContent.workspaceId, deleteWorkspaceId);
|
|
355
560
|
assert.equal(deletedWorkspace.structuredContent.action, "delete");
|
|
561
|
+
assert.equal(existsSync(deleteTaskStatePath), false);
|
|
356
562
|
assert.equal(readFileSync(join(lifecycleDeleteWorkspace, "keep.txt"), "utf8"), "keep checkout files\n");
|
|
357
563
|
const deletedInventory = callTool(oauth.accessToken, sessionId, 94, "open_workspace", {
|
|
358
564
|
action: "list",
|
|
359
565
|
workspaceId: deleteWorkspaceId,
|
|
360
566
|
});
|
|
361
567
|
assert.equal(deletedInventory.structuredContent.workspaces.length, 0);
|
|
568
|
+
pass(
|
|
569
|
+
"workspace tasks",
|
|
570
|
+
`${workspaceId} create -> external reload -> close/reopen; explicit delete removed ${deleteWorkspaceId} Task state`,
|
|
571
|
+
);
|
|
362
572
|
|
|
363
573
|
pass(
|
|
364
574
|
"workspace lifecycle + inventory",
|
|
@@ -440,6 +650,7 @@ try {
|
|
|
440
650
|
"host-integration",
|
|
441
651
|
"shell-processes",
|
|
442
652
|
"code-intelligence",
|
|
653
|
+
"workspace-tasks",
|
|
443
654
|
"batch-execution",
|
|
444
655
|
]);
|
|
445
656
|
const hooksGuide = callTool(oauth.accessToken, sessionId, 78, "read", {
|
|
@@ -650,6 +861,18 @@ try {
|
|
|
650
861
|
});
|
|
651
862
|
assert.equal(closedWorktreeInventory.structuredContent.workspaces.length, 1);
|
|
652
863
|
assert.equal(closedWorktreeInventory.structuredContent.workspaces[0].state, "closed");
|
|
864
|
+
const closedWorktreeInspection = callTool(oauth.accessToken, sessionId, 195, "open_workspace", {
|
|
865
|
+
action: "inspect",
|
|
866
|
+
workspaceId: worktreeWorkspaceId,
|
|
867
|
+
});
|
|
868
|
+
const closedWorktreeProjection = closedWorktreeInspection.structuredContent.inspection;
|
|
869
|
+
assert.equal(closedWorktreeProjection.kind, "workspace");
|
|
870
|
+
assert.equal(closedWorktreeProjection.mode, "worktree");
|
|
871
|
+
assert.equal(closedWorktreeProjection.managed, true);
|
|
872
|
+
assert.equal(closedWorktreeProjection.state, "closed");
|
|
873
|
+
assert.equal(closedWorktreeProjection.rootValid, false);
|
|
874
|
+
assert.equal(existsSync(managedWorktreePath), false);
|
|
875
|
+
assert.equal(JSON.stringify(closedWorktreeInspection).includes("\"fingerprint\""), false);
|
|
653
876
|
|
|
654
877
|
const reopenedWorktree = callTool(oauth.accessToken, sessionId, 111, "open_workspace", {
|
|
655
878
|
workspaceId: worktreeWorkspaceId,
|
|
@@ -697,6 +920,33 @@ try {
|
|
|
697
920
|
context: "none",
|
|
698
921
|
});
|
|
699
922
|
const compositeWorkspaceId = compositeOpened.structuredContent.workspaceId;
|
|
923
|
+
assert.deepEqual(
|
|
924
|
+
compositeOpened.structuredContent.capabilityCatalog.map((entry) => entry.name),
|
|
925
|
+
["workspace.tasks"],
|
|
926
|
+
);
|
|
927
|
+
const compositeTaskStatePath = join(stateDir, "workspaces", compositeWorkspaceId, "tasks.json");
|
|
928
|
+
assert.ok(existsSync(compositeTaskStatePath));
|
|
929
|
+
const compositeTaskList = callTool(oauth.accessToken, sessionId, 136, "capability", {
|
|
930
|
+
workspaceId: compositeWorkspaceId,
|
|
931
|
+
name: "workspace.tasks",
|
|
932
|
+
action: "run",
|
|
933
|
+
arguments: { operation: "list.create", name: "Composite release tasks" },
|
|
934
|
+
});
|
|
935
|
+
assert.equal(compositeTaskList.isError, undefined);
|
|
936
|
+
const compositeTaskListId = compositeTaskList.structuredContent.result.lists[0].id;
|
|
937
|
+
const compositeTask = callTool(oauth.accessToken, sessionId, 137, "capability", {
|
|
938
|
+
workspaceId: compositeWorkspaceId,
|
|
939
|
+
name: "workspace.tasks",
|
|
940
|
+
action: "run",
|
|
941
|
+
arguments: {
|
|
942
|
+
operation: "task.create",
|
|
943
|
+
listId: compositeTaskListId,
|
|
944
|
+
subject: "Preserve Composite Task state",
|
|
945
|
+
content: "Composite-owned state",
|
|
946
|
+
},
|
|
947
|
+
});
|
|
948
|
+
assert.equal(compositeTask.isError, undefined);
|
|
949
|
+
const compositeTaskId = compositeTask.structuredContent.result.lists[0].tasks[0].id;
|
|
700
950
|
callTool(oauth.accessToken, sessionId, 117, "open_workspace", {
|
|
701
951
|
action: "member",
|
|
702
952
|
workspaceId: compositeWorkspaceId,
|
|
@@ -707,6 +957,14 @@ try {
|
|
|
707
957
|
workspaceId,
|
|
708
958
|
},
|
|
709
959
|
});
|
|
960
|
+
const memberScopedCompositeTasks = callTool(oauth.accessToken, sessionId, 138, "capability", {
|
|
961
|
+
workspaceId: compositeWorkspaceId,
|
|
962
|
+
member: "code",
|
|
963
|
+
name: "workspace.tasks",
|
|
964
|
+
action: "run",
|
|
965
|
+
arguments: { operation: "get" },
|
|
966
|
+
});
|
|
967
|
+
assert.equal(memberScopedCompositeTasks.isError, true);
|
|
710
968
|
const closedComposite = callTool(oauth.accessToken, sessionId, 118, "close_workspace", {
|
|
711
969
|
workspaceId: compositeWorkspaceId,
|
|
712
970
|
});
|
|
@@ -727,6 +985,27 @@ try {
|
|
|
727
985
|
path: "composite-sentinel.txt",
|
|
728
986
|
});
|
|
729
987
|
assert.equal(closedCompositeRead.isError, true);
|
|
988
|
+
const closedCompositeTasks = callTool(oauth.accessToken, sessionId, 139, "capability", {
|
|
989
|
+
workspaceId: compositeWorkspaceId,
|
|
990
|
+
name: "workspace.tasks",
|
|
991
|
+
action: "run",
|
|
992
|
+
arguments: { operation: "get" },
|
|
993
|
+
});
|
|
994
|
+
assert.equal(closedCompositeTasks.isError, true);
|
|
995
|
+
const closedCompositeInspection = callTool(oauth.accessToken, sessionId, 196, "open_workspace", {
|
|
996
|
+
action: "inspect",
|
|
997
|
+
workspaceId: compositeWorkspaceId,
|
|
998
|
+
});
|
|
999
|
+
const closedCompositeProjection = closedCompositeInspection.structuredContent.inspection;
|
|
1000
|
+
assert.equal(closedCompositeProjection.kind, "composite");
|
|
1001
|
+
assert.equal(closedCompositeProjection.state, "closed");
|
|
1002
|
+
assert.equal(closedCompositeProjection.members[0].workspaceId, workspaceId);
|
|
1003
|
+
assert.equal(closedCompositeProjection.members[0].known, true);
|
|
1004
|
+
assert.equal(closedCompositeProjection.taskSummary.lists[0].taskCount, 1);
|
|
1005
|
+
assert.equal(
|
|
1006
|
+
JSON.stringify(closedCompositeInspection).includes("Composite-owned state"),
|
|
1007
|
+
false,
|
|
1008
|
+
);
|
|
730
1009
|
|
|
731
1010
|
const reopenedComposite = callTool(oauth.accessToken, sessionId, 121, "open_workspace", {
|
|
732
1011
|
workspaceId: compositeWorkspaceId,
|
|
@@ -735,6 +1014,20 @@ try {
|
|
|
735
1014
|
assert.equal(reopenedComposite.structuredContent.workspaceId, compositeWorkspaceId);
|
|
736
1015
|
assert.equal(reopenedComposite.structuredContent.status, "active");
|
|
737
1016
|
assert.equal(reopenedComposite.structuredContent.members[0].workspaceId, workspaceId);
|
|
1017
|
+
const reopenedCompositeTasks = callTool(oauth.accessToken, sessionId, 140, "capability", {
|
|
1018
|
+
workspaceId: compositeWorkspaceId,
|
|
1019
|
+
name: "workspace.tasks",
|
|
1020
|
+
action: "run",
|
|
1021
|
+
arguments: {
|
|
1022
|
+
operation: "get",
|
|
1023
|
+
level: "detail",
|
|
1024
|
+
listId: compositeTaskListId,
|
|
1025
|
+
taskId: compositeTaskId,
|
|
1026
|
+
},
|
|
1027
|
+
});
|
|
1028
|
+
assert.equal(reopenedCompositeTasks.isError, undefined);
|
|
1029
|
+
assert.equal(reopenedCompositeTasks.structuredContent.result.task.id, compositeTaskId);
|
|
1030
|
+
assert.equal(reopenedCompositeTasks.structuredContent.result.task.content, "Composite-owned state");
|
|
738
1031
|
const reopenedCompositeRead = callTool(oauth.accessToken, sessionId, 122, "read", {
|
|
739
1032
|
workspaceId: compositeWorkspaceId,
|
|
740
1033
|
member: "code",
|
|
@@ -748,6 +1041,7 @@ try {
|
|
|
748
1041
|
});
|
|
749
1042
|
assert.equal(deletedComposite.structuredContent.action, "delete");
|
|
750
1043
|
assert.equal(deletedComposite.structuredContent.dissolved, true);
|
|
1044
|
+
assert.equal(existsSync(compositeTaskStatePath), false);
|
|
751
1045
|
const memberAfterCompositeDelete = callTool(oauth.accessToken, sessionId, 124, "read", {
|
|
752
1046
|
workspaceId,
|
|
753
1047
|
path: "composite-sentinel.txt",
|
|
@@ -759,6 +1053,10 @@ try {
|
|
|
759
1053
|
workspaceId: compositeWorkspaceId,
|
|
760
1054
|
});
|
|
761
1055
|
assert.equal(deletedCompositeInventory.structuredContent.compositeWorkspaces.length, 0);
|
|
1056
|
+
pass(
|
|
1057
|
+
"Composite workspace tasks",
|
|
1058
|
+
`${compositeWorkspaceId} self-owned Task state -> close/reopen -> delete cleanup; member-scoped Task access rejected`,
|
|
1059
|
+
);
|
|
762
1060
|
pass(
|
|
763
1061
|
"Composite lifecycle",
|
|
764
1062
|
`${compositeWorkspaceId} close -> closed/non-routable -> same-id reopen -> delete; member Workspace preserved`,
|