@ours.network/fleet 1.1.0-nightly.8 → 1.1.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 +231 -46
- package/dist/application/capabilities.js +18 -6
- package/dist/application/fleet-query-service.js +7 -4
- package/dist/application/role-command-service.js +9 -0
- package/dist/application/role-creation-service.d.ts +4 -4
- package/dist/application/role-creation-service.js +9 -2
- package/dist/application/role-repository.d.ts +4 -2
- package/dist/application/role-repository.js +6 -3
- package/dist/application/session-control.d.ts +3 -1
- package/dist/application/session-control.js +4 -2
- package/dist/application/task-room-service.d.ts +92 -39
- package/dist/application/task-room-service.js +579 -173
- package/dist/briefing.js +85 -28
- package/dist/build-info.json +5 -5
- package/dist/canonical-json.d.ts +2 -0
- package/dist/canonical-json.js +10 -0
- package/dist/cli.js +130 -55
- package/dist/config.d.ts +38 -3
- package/dist/config.js +193 -28
- package/dist/creation.d.ts +1 -1
- package/dist/creation.js +7 -4
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +193 -55
- package/dist/doctor.d.ts +2 -0
- package/dist/doctor.js +40 -4
- package/dist/fleet-command-audit.d.ts +59 -9
- package/dist/fleet-command-audit.js +426 -63
- package/dist/fleet-proxy.d.ts +5 -2
- package/dist/harness/claude-code-session.js +2 -1
- package/dist/harness/codex-app-server-proxy.d.ts +40 -0
- package/dist/harness/codex-app-server-proxy.js +348 -14
- package/dist/harness/codex-session.d.ts +9 -2
- package/dist/harness/codex-session.js +18 -2
- package/dist/harness/codex.d.ts +5 -1
- package/dist/harness/codex.js +104 -10
- package/dist/harness/types.d.ts +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/init-guidance.d.ts +1 -1
- package/dist/init-guidance.js +1 -1
- package/dist/init-wizard.d.ts +98 -0
- package/dist/init-wizard.js +631 -0
- package/dist/lifecycle-summary.d.ts +134 -0
- package/dist/lifecycle-summary.js +179 -0
- package/dist/loops/config.d.ts +10 -1
- package/dist/loops/config.js +38 -5
- package/dist/model-recovery.d.ts +1 -1
- package/dist/ops.js +2 -17
- package/dist/owner-channel/channel.d.ts +21 -5
- package/dist/owner-channel/channel.js +312 -138
- package/dist/owner-channel/commands.d.ts +28 -6
- package/dist/owner-channel/commands.js +109 -27
- package/dist/owner-channel/lifecycle-outbox.d.ts +24 -0
- package/dist/owner-channel/lifecycle-outbox.js +87 -0
- package/dist/owner-channel/ours-client.d.ts +19 -0
- package/dist/owner-channel/ours-client.js +3 -0
- package/dist/owner-channel/state.d.ts +2 -0
- package/dist/owner-channel/state.js +8 -2
- package/dist/permissions.js +1 -1
- package/dist/preset-migration.d.ts +42 -0
- package/dist/preset-migration.js +450 -0
- package/dist/reports/artifact.d.ts +10 -0
- package/dist/reports/artifact.js +32 -0
- package/dist/reports/index.d.ts +5 -0
- package/dist/reports/index.js +5 -0
- package/dist/reports/render.d.ts +6 -0
- package/dist/reports/render.js +240 -0
- package/dist/reports/service.d.ts +42 -0
- package/dist/reports/service.js +57 -0
- package/dist/reports/tasks.d.ts +52 -0
- package/dist/reports/tasks.js +177 -0
- package/dist/reports/types.d.ts +315 -0
- package/dist/reports/types.js +6 -0
- package/dist/resolved-plan.js +13 -1
- package/dist/rooms-tasks/cli.d.ts +2 -0
- package/dist/rooms-tasks/cli.js +434 -279
- package/dist/rooms-tasks/close.d.ts +9 -1
- package/dist/rooms-tasks/close.js +23 -7
- package/dist/rooms-tasks/config.js +16 -25
- package/dist/rooms-tasks/cowork-adapter.d.ts +5 -0
- package/dist/rooms-tasks/cowork-adapter.js +13 -0
- package/dist/rooms-tasks/deletion.d.ts +51 -0
- package/dist/rooms-tasks/deletion.js +217 -0
- package/dist/rooms-tasks/external-worker.d.ts +8 -0
- package/dist/rooms-tasks/external-worker.js +38 -0
- package/dist/rooms-tasks/launch-snapshot.d.ts +19 -0
- package/dist/rooms-tasks/launch-snapshot.js +181 -0
- package/dist/rooms-tasks/member-overrides.d.ts +30 -0
- package/dist/rooms-tasks/member-overrides.js +204 -0
- package/dist/rooms-tasks/member-startup.d.ts +1 -0
- package/dist/rooms-tasks/member-startup.js +9 -2
- package/dist/rooms-tasks/provision.js +146 -56
- package/dist/rooms-tasks/room-state.d.ts +1 -0
- package/dist/rooms-tasks/room-state.js +12 -1
- package/dist/rooms-tasks/task-room-name.d.ts +12 -0
- package/dist/rooms-tasks/task-room-name.js +61 -0
- package/dist/rooms-tasks/task-state.d.ts +115 -3
- package/dist/rooms-tasks/task-state.js +361 -11
- package/dist/rooms-tasks/templates.d.ts +4 -1
- package/dist/rooms-tasks/templates.js +34 -3
- package/dist/rooms-tasks/terminal.d.ts +3 -0
- package/dist/rooms-tasks/terminal.js +10 -4
- package/dist/rooms-tasks/types.d.ts +92 -7
- package/dist/rooms-tasks/types.js +14 -1
- package/dist/runner.d.ts +3 -0
- package/dist/runner.js +92 -33
- package/dist/session/acp.d.ts +4 -1
- package/dist/session/acp.js +29 -13
- package/dist/session/arbiter.d.ts +2 -1
- package/dist/session/arbiter.js +2 -0
- package/dist/session/codex-app-server-transport.d.ts +53 -0
- package/dist/session/codex-app-server-transport.js +184 -0
- package/dist/session/codex-app-server.d.ts +122 -0
- package/dist/session/codex-app-server.js +1031 -0
- package/dist/session/control.d.ts +2 -2
- package/dist/session/control.js +18 -2
- package/dist/session/conversation-types.d.ts +4 -4
- package/dist/session/types.d.ts +24 -6
- package/dist/session/types.js +28 -0
- package/dist/spawn.d.ts +9 -0
- package/dist/spawn.js +77 -9
- package/dist/web/fleet-config-service.d.ts +1 -0
- package/dist/web/fleet-config-service.js +25 -7
- package/dist/web/runtime.js +11 -6
- package/dist/web/server.js +28 -1
- package/dist/web-app/assets/{index-BbTc5KtZ.js → index-C0h3ALvs.js} +8 -8
- package/dist/web-app/index.html +1 -1
- package/package.json +4 -4
- package/presets/brain-catalog.json +17 -0
- package/presets/fleet/{agents → agent_templates}/Critic.yaml +2 -0
- package/presets/fleet/{agents/Agent.yaml → agent_templates/Developer.yaml} +3 -1
- package/presets/fleet/agent_templates/LocalCoordinator.yaml +50 -0
- package/presets/fleet/agents/{Developer.yaml → FleetCoordinator.yaml} +1 -1
- package/presets/fleet/brains/claude-fable-5-high.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-low.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-max.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-medium.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-high.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-low.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-max.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-medium.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-sonnet-5-high.yaml +5 -0
- package/presets/fleet/brains/claude-sonnet-5-low.yaml +5 -0
- package/presets/fleet/brains/claude-sonnet-5-max.yaml +5 -0
- package/presets/fleet/brains/claude-sonnet-5-medium.yaml +5 -0
- package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-4-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-4-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-5-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-5-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +5 -0
- package/presets/fleet/roles/Coordinator.yaml +37 -0
- package/presets/fleet/roles/Critic.yaml +23 -1
- package/presets/fleet/roles/Developer.yaml +27 -5
- package/presets/fleet/roles/LocalCoordinator.yaml +35 -0
- package/presets/fleet/room_templates/pair.yaml +9 -7
- package/presets/fleet/room_templates/single.yaml +5 -4
- package/presets/fleet/room_templates/team.yaml +12 -8
- package/presets/manifest.json +2 -1
- package/presets/fleet/agents/Architect.yaml +0 -3
- package/presets/fleet/agents/Secretary.yaml +0 -3
- package/presets/fleet/agents/Tester.yaml +0 -3
- package/presets/fleet/roles/Agent.yaml +0 -6
- package/presets/fleet/roles/Architect.yaml +0 -6
- package/presets/fleet/roles/Secretary.yaml +0 -6
- package/presets/fleet/roles/Tester.yaml +0 -6
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { InterruptOutcome, SessionEvent, SessionSnapshot } from '../session/types.js';
|
|
2
2
|
import type { RoomOrchestrationRecord, TaskOutcome, TaskRecord, TaskTerminalIntent, TaskListRecord } from '../rooms-tasks/types.js';
|
|
3
3
|
import { type OwnerCommentsState } from './notices.js';
|
|
4
|
-
import type { CreateRoomRequest, CreateTaskRequest, TaskRoomApplicationService } from '../application/task-room-service.js';
|
|
4
|
+
import type { CreateRoomRequest, CreateTaskRequest, TaskProvisioningOutcome, TaskRoomApplicationService } from '../application/task-room-service.js';
|
|
5
5
|
import type { TaskState } from '../rooms-tasks/types.js';
|
|
6
6
|
/**
|
|
7
7
|
* Fleet-level effects a deterministic owner command may trigger. Production
|
|
@@ -17,7 +17,10 @@ export interface OwnerFleetOps {
|
|
|
17
17
|
closeRoom(roomId: string): Promise<void>;
|
|
18
18
|
/** Resume a task terminal intent outside the caller role's supervisor lifecycle. */
|
|
19
19
|
settleTask(taskId: string): Promise<void>;
|
|
20
|
-
|
|
20
|
+
/** Settle an accepted task deletion outside the caller role's supervisor lifecycle. */
|
|
21
|
+
settleTaskDeletion(taskId: string): Promise<void>;
|
|
22
|
+
/** Continue an accepted provisioning saga until convergence. */
|
|
23
|
+
provisionTask(taskId: string): Promise<void>;
|
|
21
24
|
}
|
|
22
25
|
/**
|
|
23
26
|
* The narrow capability surface a command executor sees. Everything here is
|
|
@@ -25,6 +28,7 @@ export interface OwnerFleetOps {
|
|
|
25
28
|
* cannot name another agent or another recipient.
|
|
26
29
|
*/
|
|
27
30
|
export interface OwnerCommandContext {
|
|
31
|
+
authenticatedCid?: string;
|
|
28
32
|
role: string;
|
|
29
33
|
/** Harness id of the role (e.g. 'claude-code', 'codex'); gates forwarding. */
|
|
30
34
|
harness: string;
|
|
@@ -50,12 +54,11 @@ export interface OwnerCommandContext {
|
|
|
50
54
|
fleetList(): Promise<string>;
|
|
51
55
|
/** Persist acceptance, acknowledge it, then launch the external close worker. */
|
|
52
56
|
closeRoom(roomId: string): Promise<void>;
|
|
53
|
-
recoverRoom(roomId: string): Promise<void>;
|
|
54
57
|
/** Persist terminal intent, acknowledge it, then launch the external settle worker. */
|
|
55
58
|
terminalTask(taskId: string, kind: TaskTerminalIntent['kind'], outcome?: TaskOutcome): Promise<void>;
|
|
56
|
-
recoverTask(taskId: string): Promise<void>;
|
|
57
59
|
createTask(input: Omit<CreateTaskRequest, 'actor'>): Promise<TaskRecord>;
|
|
58
|
-
startTask(taskId: string): Promise<TaskRecord>;
|
|
60
|
+
startTask(taskId: string): Promise<TaskProvisioningOutcome | TaskRecord>;
|
|
61
|
+
taskProvisioningOutcome?(taskId: string): TaskProvisioningOutcome;
|
|
59
62
|
listTasks(filter?: {
|
|
60
63
|
state?: TaskState | TaskState[];
|
|
61
64
|
list?: string;
|
|
@@ -83,7 +86,8 @@ export interface OwnerCommandContext {
|
|
|
83
86
|
blockTask(taskId: string, reason: string): TaskRecord;
|
|
84
87
|
unblockTask(taskId: string): TaskRecord;
|
|
85
88
|
reviewTask(taskId: string): TaskRecord;
|
|
86
|
-
|
|
89
|
+
/** Accept a permanent any-state deletion, acknowledge it, then launch the external delete worker. */
|
|
90
|
+
deleteTask(taskId: string): Promise<void>;
|
|
87
91
|
listRoomQueries(filter?: {
|
|
88
92
|
state?: 'active' | 'provisioning';
|
|
89
93
|
}): ReturnType<TaskRoomApplicationService['listRooms']>;
|
|
@@ -94,6 +98,7 @@ export interface OwnerCommandContext {
|
|
|
94
98
|
recentEvents(limit: number): SessionEvent[];
|
|
95
99
|
readWorklogTail(maxChars: number): Promise<string | undefined>;
|
|
96
100
|
reply(text: string): Promise<void>;
|
|
101
|
+
replyHtml(filename: string, html: string): Promise<void>;
|
|
97
102
|
}
|
|
98
103
|
export interface OwnerCommand {
|
|
99
104
|
/** Primary name without the leading slash. */
|
|
@@ -105,6 +110,11 @@ export interface OwnerCommand {
|
|
|
105
110
|
summary: string;
|
|
106
111
|
execute(ctx: OwnerCommandContext, args: string): Promise<void>;
|
|
107
112
|
}
|
|
113
|
+
export interface OwnerTypedCommandDefinition {
|
|
114
|
+
name: string;
|
|
115
|
+
description: string;
|
|
116
|
+
input_schema: Record<string, unknown>;
|
|
117
|
+
}
|
|
108
118
|
/**
|
|
109
119
|
* Commands each harness's bundled ACP adapter verifiably executes locally,
|
|
110
120
|
* pinned by test/acp-adapter-commands.test.ts against the shipped adapter
|
|
@@ -121,6 +131,18 @@ export declare const HARNESS_LOCAL_COMMANDS: Record<string, readonly string[]>;
|
|
|
121
131
|
* registration step for a new command.
|
|
122
132
|
*/
|
|
123
133
|
export declare const ownerCommands: OwnerCommand[];
|
|
134
|
+
/**
|
|
135
|
+
* Project the slash-command registry into the SDK catalog. Only primary names
|
|
136
|
+
* are advertised: aliases remain accepted by the slash dispatcher without
|
|
137
|
+
* cluttering the typed menu with duplicate actions.
|
|
138
|
+
*
|
|
139
|
+
* Typed arguments deliberately stay as the exact text following the command
|
|
140
|
+
* name. That adapter is what lets typed and slash invocations share every
|
|
141
|
+
* existing parser, usage error, lifecycle guard, and result path.
|
|
142
|
+
*/
|
|
143
|
+
export declare function ownerTypedCommandCatalog(): OwnerTypedCommandDefinition[];
|
|
144
|
+
/** Convert one typed invocation back into the canonical slash input. */
|
|
145
|
+
export declare function ownerTypedCommandText(name: string, input: unknown): string;
|
|
124
146
|
/** Trimmed slash-prefixed text is a command attempt and is never forwarded. */
|
|
125
147
|
export declare const isOwnerCommandText: (text: string) => boolean;
|
|
126
148
|
export declare function ownerCommandHelp(error?: string): string;
|
|
@@ -6,6 +6,16 @@ import { markdownCode, markdownProse, renderMarkdownFailure, renderMarkdownList,
|
|
|
6
6
|
import { OWNER_COMMENT_LABEL, ownerNotices } from './notices.js';
|
|
7
7
|
import { TaskRoomApplicationError } from '../application/task-room-service.js';
|
|
8
8
|
import { TaskListError } from '../rooms-tasks/task-lists.js';
|
|
9
|
+
import { createTasksReport } from '../reports/index.js';
|
|
10
|
+
async function ownerTaskReport(ctx) {
|
|
11
|
+
return createTasksReport({
|
|
12
|
+
viewer: { surface: 'messenger', authenticatedCid: ctx.authenticatedCid ?? 'owner-command-context', roomCids: [] },
|
|
13
|
+
collect: () => {
|
|
14
|
+
return { lists: ctx.listTaskLists(), tasks: ctx.listTasks() };
|
|
15
|
+
},
|
|
16
|
+
source: { name: 'ours-fleet', version: ctx.version },
|
|
17
|
+
});
|
|
18
|
+
}
|
|
9
19
|
/** A malformed invocation; the dispatcher answers it with annotated help. */
|
|
10
20
|
class OwnerCommandUsageError extends Error {
|
|
11
21
|
}
|
|
@@ -26,7 +36,7 @@ function isKnownOwnerTaskState(message) {
|
|
|
26
36
|
/^task is not blocked$/u,
|
|
27
37
|
new RegExp(`^task ${SAFE_ID_WORD} already has a conflicting '(?:done|cancelled)' terminal intent$`, 'u'),
|
|
28
38
|
new RegExp(`^task ${SAFE_ID_WORD} is already in terminal state '${TASK_STATE_WORD}'$`, 'u'),
|
|
29
|
-
new RegExp(`^
|
|
39
|
+
new RegExp(`^task ${SAFE_ID_WORD} is pending deletion; run 'ours-fleet task delete ${SAFE_ID_WORD} ${SAFE_ID_WORD}' to retry cleanup$`, 'u'),
|
|
30
40
|
].some(pattern => pattern.test(message));
|
|
31
41
|
}
|
|
32
42
|
function isKnownOwnerRoomState(message) {
|
|
@@ -96,6 +106,22 @@ const taskAction = (title, task, fields = []) => renderMarkdownResult({
|
|
|
96
106
|
...fields,
|
|
97
107
|
],
|
|
98
108
|
});
|
|
109
|
+
const taskProvisioningAction = (outcome) => {
|
|
110
|
+
if (outcome.kind === 'failed')
|
|
111
|
+
return renderMarkdownFailure({
|
|
112
|
+
kind: 'state', subject: `/task start ${outcome.task.task_id}`,
|
|
113
|
+
detail: outcome.blocker ?? 'Provisioning reached a terminal failure.',
|
|
114
|
+
action: outcome.next_action ?? `Run /task start ${outcome.task.task_id}.`,
|
|
115
|
+
});
|
|
116
|
+
return taskAction(outcome.kind === 'ready' ? 'Room provisioning complete'
|
|
117
|
+
: outcome.next_action ? 'Room provisioning needs attention' : 'Room provisioning continues', outcome.task, [
|
|
118
|
+
...(outcome.room ? [{ label: 'Room', value: `${outcome.room.room_id} — ${outcome.room.room_name}` }] : []),
|
|
119
|
+
...(outcome.launch.template ? [{ label: 'Template', value: outcome.launch.template, kind: 'code' }] : []),
|
|
120
|
+
{ label: 'Members', value: `${outcome.members.active}/${outcome.members.expected} active; ${outcome.members.launched}/${outcome.members.expected} launched` },
|
|
121
|
+
...(outcome.blocker ? [{ label: 'Blocker', value: outcome.blocker }] : []),
|
|
122
|
+
...(outcome.next_action ? [{ label: 'Next action', value: outcome.next_action }] : []),
|
|
123
|
+
]);
|
|
124
|
+
};
|
|
99
125
|
const roomAction = (title, room, fields = []) => renderMarkdownResult({
|
|
100
126
|
icon: '🏠', title,
|
|
101
127
|
fields: [
|
|
@@ -242,10 +268,18 @@ export const ownerCommands = [
|
|
|
242
268
|
execute: noArgs('/version', async (ctx) => ctx.reply(`ℹ️ ours-fleet ${ctx.version}`)),
|
|
243
269
|
},
|
|
244
270
|
{
|
|
245
|
-
name: 'tasks', usage: '/tasks [state]',
|
|
246
|
-
summary: 'list tasks
|
|
271
|
+
name: 'tasks', usage: '/tasks [state|html]',
|
|
272
|
+
summary: 'list tasks, or attach the HTML report with /tasks html',
|
|
247
273
|
execute: async (ctx, args) => {
|
|
248
274
|
const stateFilter = args?.trim() || undefined;
|
|
275
|
+
if (stateFilter === 'html') {
|
|
276
|
+
const artifact = await ownerTaskReport(ctx);
|
|
277
|
+
await ctx.replyHtml(artifact.metadata.filename, artifact.html);
|
|
278
|
+
await ctx.reply(`HTML report attached: ${artifact.metadata.filename}`);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (stateFilter && /\s/u.test(stateFilter))
|
|
282
|
+
throw new OwnerCommandUsageError('usage: /tasks [state|html]');
|
|
249
283
|
const tasks = ctx.listTasks(stateFilter && stateFilter !== 'all' ? { state: stateFilter } : undefined);
|
|
250
284
|
await ctx.reply(renderMarkdownList({
|
|
251
285
|
icon: '📋', title: 'Tasks', empty: 'No tasks found.',
|
|
@@ -255,7 +289,7 @@ export const ownerCommands = [
|
|
|
255
289
|
},
|
|
256
290
|
{
|
|
257
291
|
name: 'task',
|
|
258
|
-
usage: '/task <create|list|show|start|block|unblock|review|done|cancel|delete
|
|
292
|
+
usage: '/task <create|list|show|start|block|unblock|review|done|cancel|delete> ...',
|
|
259
293
|
summary: 'task lifecycle subcommands',
|
|
260
294
|
execute: async (ctx, args) => {
|
|
261
295
|
if (!args)
|
|
@@ -301,6 +335,12 @@ export const ownerCommands = [
|
|
|
301
335
|
title: titleParts.join(' '), origin: { type: 'owner_channel' },
|
|
302
336
|
backlog, template, noRoom, brief: trailingLines, list,
|
|
303
337
|
});
|
|
338
|
+
const provisioning = !backlog && !noRoom && t.room_id
|
|
339
|
+
? ctx.taskProvisioningOutcome?.(t.task_id) : undefined;
|
|
340
|
+
if (provisioning) {
|
|
341
|
+
await ctx.reply(taskProvisioningAction(provisioning));
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
304
344
|
await ctx.reply(taskAction('Task created', t, [
|
|
305
345
|
{ label: 'Title', value: t.title },
|
|
306
346
|
...(template ? [{ label: 'Template', value: template, kind: 'code' }] : []),
|
|
@@ -309,6 +349,9 @@ export const ownerCommands = [
|
|
|
309
349
|
break;
|
|
310
350
|
}
|
|
311
351
|
case 'list': {
|
|
352
|
+
if (rest.some(value => value.startsWith('--')
|
|
353
|
+
&& value !== '--group-by-list' && !value.startsWith('--list=')))
|
|
354
|
+
throw new OwnerCommandUsageError('usage: /task list [state] [--list=<name>] [--group-by-list]');
|
|
312
355
|
const listFlag = rest.find(r => r.startsWith('--list='));
|
|
313
356
|
const list = trailingLines ?? listFlag?.slice('--list='.length);
|
|
314
357
|
const grouped = rest.includes('--group-by-list');
|
|
@@ -345,6 +388,8 @@ export const ownerCommands = [
|
|
|
345
388
|
break;
|
|
346
389
|
}
|
|
347
390
|
case 'lists': {
|
|
391
|
+
if (rest.length || trailingLines)
|
|
392
|
+
throw new OwnerCommandUsageError('usage: /task lists');
|
|
348
393
|
const lists = ctx.listTaskLists();
|
|
349
394
|
await ctx.reply(renderMarkdownList({ icon: '📚', title: 'Task lists', empty: 'No task lists found.',
|
|
350
395
|
records: lists.map(list => `${markdownCode(list.name)}${list.built_in ? ' — built-in' : ''}`) }));
|
|
@@ -388,7 +433,7 @@ export const ownerCommands = [
|
|
|
388
433
|
break;
|
|
389
434
|
}
|
|
390
435
|
case 'show': {
|
|
391
|
-
if (
|
|
436
|
+
if (rest.length !== 1 || rest[0].startsWith('--'))
|
|
392
437
|
throw new OwnerCommandUsageError('usage: /task show <id>');
|
|
393
438
|
await showTask(rest[0]);
|
|
394
439
|
break;
|
|
@@ -396,8 +441,9 @@ export const ownerCommands = [
|
|
|
396
441
|
case 'start': {
|
|
397
442
|
if (!rest[0])
|
|
398
443
|
throw new OwnerCommandUsageError('usage: /task start <id>');
|
|
399
|
-
const
|
|
400
|
-
await ctx.reply(
|
|
444
|
+
const started = await ctx.startTask(rest[0]);
|
|
445
|
+
await ctx.reply('kind' in started
|
|
446
|
+
? taskProvisioningAction(started) : taskAction('Task started', started));
|
|
401
447
|
break;
|
|
402
448
|
}
|
|
403
449
|
case 'block': {
|
|
@@ -438,21 +484,13 @@ export const ownerCommands = [
|
|
|
438
484
|
case 'delete': {
|
|
439
485
|
if (rest.length !== 2 || rest[0] !== rest[1])
|
|
440
486
|
throw new OwnerCommandUsageError('destructive: /task delete <id> <id> — provide the task ID twice');
|
|
441
|
-
|
|
442
|
-
await ctx.reply(renderMarkdownResult({
|
|
443
|
-
icon: '🗑️', title: deleted ? 'Task deleted' : 'Task already absent',
|
|
444
|
-
fields: [{ label: 'ID', value: rest[0], kind: 'code' }],
|
|
445
|
-
}));
|
|
446
|
-
break;
|
|
447
|
-
}
|
|
448
|
-
case 'recover': {
|
|
449
|
-
if (!rest[0])
|
|
450
|
-
throw new OwnerCommandUsageError('usage: /task recover <id>');
|
|
451
|
-
await ctx.recoverTask(rest[0]);
|
|
487
|
+
await ctx.deleteTask(rest[0]);
|
|
452
488
|
break;
|
|
453
489
|
}
|
|
454
490
|
default:
|
|
455
491
|
// bare /task <id> → show
|
|
492
|
+
if (sub === 'recover' || sub === 'await' || rest.length)
|
|
493
|
+
throw new OwnerCommandUsageError(`unknown task subcommand: ${sub}`);
|
|
456
494
|
await showTask(sub);
|
|
457
495
|
}
|
|
458
496
|
}
|
|
@@ -482,7 +520,7 @@ export const ownerCommands = [
|
|
|
482
520
|
},
|
|
483
521
|
{
|
|
484
522
|
name: 'room',
|
|
485
|
-
usage: '/room <create|list|show|delete|close
|
|
523
|
+
usage: '/room <create|list|show|delete|close> ...',
|
|
486
524
|
summary: 'room lifecycle subcommands',
|
|
487
525
|
execute: async (ctx, args) => {
|
|
488
526
|
if (!args)
|
|
@@ -567,14 +605,10 @@ export const ownerCommands = [
|
|
|
567
605
|
await ctx.closeRoom(rest[0]);
|
|
568
606
|
break;
|
|
569
607
|
}
|
|
570
|
-
case 'recover': {
|
|
571
|
-
if (!rest[0])
|
|
572
|
-
throw new OwnerCommandUsageError('usage: /room recover <id>');
|
|
573
|
-
await ctx.recoverRoom(rest[0]);
|
|
574
|
-
break;
|
|
575
|
-
}
|
|
576
608
|
default:
|
|
577
609
|
// bare /room <id> → show
|
|
610
|
+
if (sub === 'recover' || rest.length)
|
|
611
|
+
throw new OwnerCommandUsageError(`unknown room subcommand: ${sub}`);
|
|
578
612
|
await showRoom(sub);
|
|
579
613
|
}
|
|
580
614
|
}
|
|
@@ -628,7 +662,7 @@ export const ownerCommands = [
|
|
|
628
662
|
...(t.contract ? [`Contract: ${t.contract}`] : []),
|
|
629
663
|
'Members:',
|
|
630
664
|
...t.members.map(m => ` ${m.slot} (${m.role}) ×${m.count} → `
|
|
631
|
-
+
|
|
665
|
+
+ `Agent Template: ${m.agent_template}`),
|
|
632
666
|
];
|
|
633
667
|
await ctx.reply(lines.join('\n'));
|
|
634
668
|
};
|
|
@@ -654,6 +688,53 @@ export const ownerCommands = [
|
|
|
654
688
|
},
|
|
655
689
|
},
|
|
656
690
|
];
|
|
691
|
+
/**
|
|
692
|
+
* Project the slash-command registry into the SDK catalog. Only primary names
|
|
693
|
+
* are advertised: aliases remain accepted by the slash dispatcher without
|
|
694
|
+
* cluttering the typed menu with duplicate actions.
|
|
695
|
+
*
|
|
696
|
+
* Typed arguments deliberately stay as the exact text following the command
|
|
697
|
+
* name. That adapter is what lets typed and slash invocations share every
|
|
698
|
+
* existing parser, usage error, lifecycle guard, and result path.
|
|
699
|
+
*/
|
|
700
|
+
export function ownerTypedCommandCatalog() {
|
|
701
|
+
return ownerCommands.map(command => {
|
|
702
|
+
const usage = command.usage ?? `/${command.name}`;
|
|
703
|
+
const suffix = usage.slice(usage.indexOf(' ') + 1);
|
|
704
|
+
const acceptsArguments = usage.includes(' ');
|
|
705
|
+
const requiresArguments = acceptsArguments && !suffix.startsWith('[');
|
|
706
|
+
return {
|
|
707
|
+
name: command.name,
|
|
708
|
+
description: `${command.summary} (${usage})`,
|
|
709
|
+
input_schema: {
|
|
710
|
+
type: 'object',
|
|
711
|
+
properties: acceptsArguments ? {
|
|
712
|
+
arguments: {
|
|
713
|
+
type: 'string', title: 'Arguments',
|
|
714
|
+
description: `Text after /${command.name}. Usage: ${usage}`,
|
|
715
|
+
},
|
|
716
|
+
} : {},
|
|
717
|
+
...(requiresArguments ? { required: ['arguments'] } : {}),
|
|
718
|
+
},
|
|
719
|
+
};
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
/** Convert one typed invocation back into the canonical slash input. */
|
|
723
|
+
export function ownerTypedCommandText(name, input) {
|
|
724
|
+
const command = ownerCommands.find(entry => entry.name === name);
|
|
725
|
+
if (!command)
|
|
726
|
+
throw new Error(`unknown typed owner command: ${name}`);
|
|
727
|
+
if (!input || typeof input !== 'object' || Array.isArray(input))
|
|
728
|
+
throw new Error('typed owner command arguments must be an object');
|
|
729
|
+
const record = input;
|
|
730
|
+
if (Object.keys(record).some(key => key !== 'arguments'))
|
|
731
|
+
throw new Error('typed owner command arguments contain an unknown field');
|
|
732
|
+
const value = record.arguments;
|
|
733
|
+
if (value !== undefined && typeof value !== 'string')
|
|
734
|
+
throw new Error('typed owner command arguments must be text');
|
|
735
|
+
const args = value?.trim() ?? '';
|
|
736
|
+
return `/${command.name}${args ? ` ${args}` : ''}`;
|
|
737
|
+
}
|
|
657
738
|
/** Trimmed slash-prefixed text is a command attempt and is never forwarded. */
|
|
658
739
|
export const isOwnerCommandText = (text) => text.trim().startsWith('/');
|
|
659
740
|
export function ownerCommandHelp(error) {
|
|
@@ -711,6 +792,7 @@ export function fleetCliOps(role, configPath) {
|
|
|
711
792
|
}),
|
|
712
793
|
closeRoom: roomId => launchFleetWorker(['room', '_delete', roomId], `room-delete-${roomId}`, configPath),
|
|
713
794
|
settleTask: taskId => launchFleetWorker(['task', '_settle', taskId], `task-settle-${taskId}`, configPath),
|
|
714
|
-
|
|
795
|
+
settleTaskDeletion: taskId => launchFleetWorker(['task', '_settle_delete', taskId], `task-delete-${taskId}`, configPath),
|
|
796
|
+
provisionTask: taskId => launchFleetWorker(['task', '_provision', taskId], `task-provision-${taskId}`, configPath),
|
|
715
797
|
};
|
|
716
798
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type FleetAuditPresentation } from '../fleet-command-audit.js';
|
|
2
|
+
type LifecycleDelivery = 'pending' | 'delivered' | 'uncertain';
|
|
3
|
+
interface LifecycleOutboxEntry {
|
|
4
|
+
digest: string;
|
|
5
|
+
presentation: FleetAuditPresentation;
|
|
6
|
+
delivery: LifecycleDelivery;
|
|
7
|
+
}
|
|
8
|
+
/** Durable lifecycle acceptance queue; plaintext is limited to validated concise presentations. */
|
|
9
|
+
export declare class FleetLifecycleOutbox {
|
|
10
|
+
private readonly path;
|
|
11
|
+
private entries;
|
|
12
|
+
private corruptReason?;
|
|
13
|
+
constructor(path: string);
|
|
14
|
+
integrity(): {
|
|
15
|
+
ok: boolean;
|
|
16
|
+
error?: string;
|
|
17
|
+
};
|
|
18
|
+
enqueue(presentations: FleetAuditPresentation[]): void;
|
|
19
|
+
pending(): readonly LifecycleOutboxEntry[];
|
|
20
|
+
finish(digest: string, delivery: Exclude<LifecycleDelivery, 'pending'>): void;
|
|
21
|
+
private assertHealthy;
|
|
22
|
+
private persist;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { chmodSync, existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { replaceFileAtomically } from '../atomic-file.js';
|
|
4
|
+
import { lifecycleEventDigestBasis, validateFleetAuditPresentations, } from '../fleet-command-audit.js';
|
|
5
|
+
const DIGEST = /^[a-f0-9]{64}$/u;
|
|
6
|
+
const LIMIT = 5_000;
|
|
7
|
+
/** Durable lifecycle acceptance queue; plaintext is limited to validated concise presentations. */
|
|
8
|
+
export class FleetLifecycleOutbox {
|
|
9
|
+
path;
|
|
10
|
+
entries = [];
|
|
11
|
+
corruptReason;
|
|
12
|
+
constructor(path) {
|
|
13
|
+
this.path = path;
|
|
14
|
+
if (!existsSync(path))
|
|
15
|
+
return;
|
|
16
|
+
try {
|
|
17
|
+
const value = JSON.parse(readFileSync(path, 'utf8'));
|
|
18
|
+
if (value.version !== 1 || !Array.isArray(value.entries) || value.entries.length > LIMIT)
|
|
19
|
+
throw new Error('invalid lifecycle outbox');
|
|
20
|
+
const digests = new Set();
|
|
21
|
+
for (const entry of value.entries) {
|
|
22
|
+
validateFleetAuditPresentations([entry?.presentation]);
|
|
23
|
+
if (!DIGEST.test(entry.digest)
|
|
24
|
+
|| !['pending', 'delivered', 'uncertain'].includes(entry.delivery)
|
|
25
|
+
|| digests.has(entry.digest))
|
|
26
|
+
throw new Error('invalid lifecycle outbox entry');
|
|
27
|
+
digests.add(entry.digest);
|
|
28
|
+
}
|
|
29
|
+
this.entries = structuredClone(value.entries);
|
|
30
|
+
chmodSync(path, 0o600);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
this.corruptReason = 'invalid persisted Fleet lifecycle outbox';
|
|
34
|
+
this.entries = [];
|
|
35
|
+
try {
|
|
36
|
+
chmodSync(path, 0o600);
|
|
37
|
+
}
|
|
38
|
+
catch { /* remain fail-closed */ }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
integrity() {
|
|
42
|
+
return this.corruptReason ? { ok: false, error: this.corruptReason } : { ok: true };
|
|
43
|
+
}
|
|
44
|
+
enqueue(presentations) {
|
|
45
|
+
this.assertHealthy();
|
|
46
|
+
validateFleetAuditPresentations(presentations);
|
|
47
|
+
let changed = false;
|
|
48
|
+
for (const presentation of presentations) {
|
|
49
|
+
const digest = createHash('sha256').update(lifecycleEventDigestBasis(presentation)).digest('hex');
|
|
50
|
+
if (this.entries.some(entry => entry.digest === digest))
|
|
51
|
+
continue;
|
|
52
|
+
if (this.entries.length >= LIMIT) {
|
|
53
|
+
const settled = this.entries.findIndex(entry => entry.delivery !== 'pending');
|
|
54
|
+
if (settled < 0)
|
|
55
|
+
throw new Error(`Fleet lifecycle outbox is limited to ${LIMIT} pending events`);
|
|
56
|
+
this.entries.splice(settled, 1);
|
|
57
|
+
}
|
|
58
|
+
this.entries.push({ digest, presentation: structuredClone(presentation), delivery: 'pending' });
|
|
59
|
+
changed = true;
|
|
60
|
+
}
|
|
61
|
+
if (changed)
|
|
62
|
+
this.persist();
|
|
63
|
+
}
|
|
64
|
+
pending() {
|
|
65
|
+
this.assertHealthy();
|
|
66
|
+
return this.entries.filter(entry => entry.delivery === 'pending').map(entry => structuredClone(entry));
|
|
67
|
+
}
|
|
68
|
+
finish(digest, delivery) {
|
|
69
|
+
this.assertHealthy();
|
|
70
|
+
const entry = this.entries.find(candidate => candidate.digest === digest);
|
|
71
|
+
if (!entry)
|
|
72
|
+
throw new Error('unknown Fleet lifecycle outbox entry');
|
|
73
|
+
if (entry.delivery === delivery)
|
|
74
|
+
return;
|
|
75
|
+
if (entry.delivery !== 'pending')
|
|
76
|
+
throw new Error('conflicting Fleet lifecycle delivery state');
|
|
77
|
+
entry.delivery = delivery;
|
|
78
|
+
this.persist();
|
|
79
|
+
}
|
|
80
|
+
assertHealthy() {
|
|
81
|
+
if (this.corruptReason)
|
|
82
|
+
throw new Error(this.corruptReason);
|
|
83
|
+
}
|
|
84
|
+
persist() {
|
|
85
|
+
replaceFileAtomically(this.path, `${JSON.stringify({ version: 1, entries: this.entries })}\n`, 0o600);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -23,6 +23,22 @@ export type OursRetrievedFiles = Res<'getFiles'>;
|
|
|
23
23
|
export type OursRetrievedFile = OursRetrievedFiles['files'][number];
|
|
24
24
|
export type OursHistoryFile = NonNullable<Res<'getFileInfo'>>;
|
|
25
25
|
export type OursNotificationEvent = NotificationEvent;
|
|
26
|
+
export type OursJsonValue = null | boolean | number | string | OursJsonValue[] | {
|
|
27
|
+
[key: string]: OursJsonValue;
|
|
28
|
+
};
|
|
29
|
+
export interface OursCommandContext {
|
|
30
|
+
readonly sender_cid: string;
|
|
31
|
+
readonly sender_name: string;
|
|
32
|
+
readonly request_wire_id: string;
|
|
33
|
+
}
|
|
34
|
+
export interface OursRegisteredCommand {
|
|
35
|
+
name: string;
|
|
36
|
+
description?: string;
|
|
37
|
+
input_schema: {
|
|
38
|
+
[key: string]: OursJsonValue;
|
|
39
|
+
};
|
|
40
|
+
handler(argumentsValue: OursJsonValue, context: Readonly<OursCommandContext>): OursJsonValue | Promise<OursJsonValue>;
|
|
41
|
+
}
|
|
26
42
|
export declare class OursWatchDeadlineError extends OursDaemonError {
|
|
27
43
|
}
|
|
28
44
|
/**
|
|
@@ -41,6 +57,8 @@ export interface OursOps {
|
|
|
41
57
|
start(): Promise<void>;
|
|
42
58
|
/** Bind this session's identity. Throws `BOUND_ELSEWHERE` when it is held live. */
|
|
43
59
|
bindIdentity(name: string): Promise<void>;
|
|
60
|
+
/** Replace the bound identity's advertised typed-command catalog and handlers. */
|
|
61
|
+
registerCommands(commands: OursRegisteredCommand[]): Promise<void>;
|
|
44
62
|
listContacts(): Promise<OursContactsView>;
|
|
45
63
|
generateInvite(name?: string): Promise<OursInviteResult>;
|
|
46
64
|
addContact(a: {
|
|
@@ -114,6 +132,7 @@ export declare class OursSdkClient implements OursOps {
|
|
|
114
132
|
constructor(env?: Record<string, string>, log?: (line: string) => void, deps?: OursSdkClientDeps);
|
|
115
133
|
start(): Promise<void>;
|
|
116
134
|
bindIdentity(name: string): Promise<void>;
|
|
135
|
+
registerCommands(commands: OursRegisteredCommand[]): Promise<void>;
|
|
117
136
|
listContacts(): Promise<OursContactsView>;
|
|
118
137
|
generateInvite(name?: string): Promise<OursInviteResult>;
|
|
119
138
|
addContact(a: {
|
|
@@ -140,6 +140,9 @@ export class OursSdkClient {
|
|
|
140
140
|
// from an identity, it waits for the bounded handoff window and then fails.
|
|
141
141
|
await this.ops().chooseIdentity({ name, force: false });
|
|
142
142
|
}
|
|
143
|
+
async registerCommands(commands) {
|
|
144
|
+
await this.ops().registerCommands(commands);
|
|
145
|
+
}
|
|
143
146
|
async listContacts() {
|
|
144
147
|
return this.ops().listContacts();
|
|
145
148
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/** A send whose (dedupe-scoped) digest was already recorded: it must not repeat. */
|
|
2
2
|
export declare class DuplicateSendError extends Error {
|
|
3
|
+
readonly status: OwnerProactiveSend['status'];
|
|
4
|
+
constructor(status: OwnerProactiveSend['status']);
|
|
3
5
|
}
|
|
4
6
|
/** Durable bounded dedupe containing wire IDs only — never message or reply plaintext. */
|
|
5
7
|
export declare class OwnerChannelState {
|
|
@@ -5,6 +5,11 @@ import { replaceFileAtomically } from '../atomic-file.js';
|
|
|
5
5
|
import { canonicalCid } from '../config.js';
|
|
6
6
|
/** A send whose (dedupe-scoped) digest was already recorded: it must not repeat. */
|
|
7
7
|
export class DuplicateSendError extends Error {
|
|
8
|
+
status;
|
|
9
|
+
constructor(status) {
|
|
10
|
+
super('duplicate proactive owner message refused');
|
|
11
|
+
this.status = status;
|
|
12
|
+
}
|
|
8
13
|
}
|
|
9
14
|
/** Durable bounded dedupe containing wire IDs only — never message or reply plaintext. */
|
|
10
15
|
export class OwnerChannelState {
|
|
@@ -184,8 +189,9 @@ export class OwnerConversationState {
|
|
|
184
189
|
// 'all' scope serves wire-keyed idempotency: a crash replay must not
|
|
185
190
|
// deliver the same wire to a second owner after the route moved.
|
|
186
191
|
const scope = dedupe === 'all' ? this.sends : recent;
|
|
187
|
-
|
|
188
|
-
|
|
192
|
+
const duplicate = scope.find(send => send.digest === digest);
|
|
193
|
+
if (duplicate)
|
|
194
|
+
throw new DuplicateSendError(duplicate.status);
|
|
189
195
|
const last = recent.at(-1);
|
|
190
196
|
if (last && now - last.at < minIntervalMs)
|
|
191
197
|
throw new Error(`proactive owner messages are rate-limited to one every ${minIntervalMs}ms`);
|
package/dist/permissions.js
CHANGED
|
@@ -85,7 +85,7 @@ export function analyzeRolePermissions(role) {
|
|
|
85
85
|
translation = { ...translation, capabilities: inspection.capabilities };
|
|
86
86
|
}
|
|
87
87
|
const conflicts = neutral.supported
|
|
88
|
-
? findConflicts(role, neutral.native, adapter.nativePermissionOverrides(role.harness_options))
|
|
88
|
+
? findConflicts(role, neutral.native, adapter.nativePermissionOverrides(role.harness_options, role))
|
|
89
89
|
: [];
|
|
90
90
|
const floor = checkUnattendedFloor(translation.capabilities, requiredUnattendedFloor(role));
|
|
91
91
|
const floorSeverity = role.permissions.unattended === 'deny' ? 'fail' : 'warn';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { renameSync } from 'node:fs';
|
|
2
|
+
export declare const legacyStarterMigrationManifest: Readonly<{
|
|
3
|
+
schemaVersion: 1;
|
|
4
|
+
presetRevision: 2;
|
|
5
|
+
fingerprints: Readonly<Record<string, string>>;
|
|
6
|
+
}>;
|
|
7
|
+
export interface PresetMigrationResult {
|
|
8
|
+
write: boolean;
|
|
9
|
+
root: string;
|
|
10
|
+
stagingPath: string;
|
|
11
|
+
backupPath: string;
|
|
12
|
+
moves: Array<{
|
|
13
|
+
from: string;
|
|
14
|
+
to: string;
|
|
15
|
+
}>;
|
|
16
|
+
additions: string[];
|
|
17
|
+
}
|
|
18
|
+
export interface RoleDefaultMigrationResult {
|
|
19
|
+
write: boolean;
|
|
20
|
+
root: string;
|
|
21
|
+
stagingPath: string;
|
|
22
|
+
backupPath: string;
|
|
23
|
+
removals: string[];
|
|
24
|
+
replacements: string[];
|
|
25
|
+
additions: string[];
|
|
26
|
+
preserved: string[];
|
|
27
|
+
}
|
|
28
|
+
interface MigrationHooks {
|
|
29
|
+
rename?: typeof renameSync;
|
|
30
|
+
nonce?: string;
|
|
31
|
+
beforeLockClaim?: () => void;
|
|
32
|
+
beforeRoleDefaultPublish?: () => void;
|
|
33
|
+
}
|
|
34
|
+
/** Explicit adoption of exact known packaged role defaults; customized files are never changed. */
|
|
35
|
+
export declare function migratePackagedRoleDefaults(configuration: string, options?: {
|
|
36
|
+
write?: boolean;
|
|
37
|
+
}, hooks?: MigrationHooks): RoleDefaultMigrationResult;
|
|
38
|
+
/** Explicit safe migration of the exact revision-2 six-worker starter set. Dry-run unless write=true. */
|
|
39
|
+
export declare function migrateLegacyStarterPresets(configuration: string, options?: {
|
|
40
|
+
write?: boolean;
|
|
41
|
+
}, hooks?: MigrationHooks): PresetMigrationResult;
|
|
42
|
+
export {};
|