@brimveyn/aimux 1.21.0 → 1.22.2
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 +27 -25
- package/package.json +2 -2
- package/src/app-runtime/auto-commit-driver.ts +22 -27
- package/src/app-runtime/backend-attach-runtime.ts +43 -36
- package/src/app-runtime/backend-runtime-events.ts +107 -88
- package/src/app-runtime/editor-actions.ts +201 -0
- package/src/app-runtime/git-actions.ts +197 -0
- package/src/app-runtime/measure-ref.ts +19 -0
- package/src/app-runtime/navigation-actions.ts +213 -0
- package/src/app-runtime/project-actions.ts +188 -0
- package/src/app-runtime/prompt-injection.ts +123 -0
- package/src/app-runtime/selection.ts +29 -0
- package/src/app-runtime/settings-actions.ts +156 -0
- package/src/app-runtime/setup-actions.ts +237 -0
- package/src/app-runtime/side-effect-context.ts +33 -0
- package/src/app-runtime/side-effects.ts +302 -1541
- package/src/app-runtime/snippet-actions.ts +3 -2
- package/src/app-runtime/tab-actions.ts +218 -0
- package/src/app-runtime/tab-runtime-timeouts.ts +1 -1
- package/src/app-runtime/use-auto-commit-driver.ts +14 -13
- package/src/app-runtime/use-backend-runtime.ts +12 -11
- package/src/app-runtime/use-directory-search.ts +5 -4
- package/src/app-runtime/use-mouse-handlers.ts +2 -1
- package/src/app-runtime/{use-workspace-autosave.ts → use-project-autosave.ts} +4 -4
- package/src/app-runtime/use-renderer-bindings.ts +2 -1
- package/src/app-runtime/use-setup-runner.ts +96 -0
- package/src/app-runtime/use-terminal-resize.ts +18 -14
- package/src/app-runtime/workspace-actions.ts +366 -0
- package/src/app-runtime/workspace-activity.ts +138 -0
- package/src/app-runtime/workspace-naming.ts +90 -0
- package/src/app.tsx +93 -48
- package/src/assets/sounds/bell.wav +0 -0
- package/src/assets/sounds/ding.wav +0 -0
- package/src/assets/sounds/plane.wav +0 -0
- package/src/cli/client/daemon-client.ts +2 -2
- package/src/cli/client/project-resolver.ts +112 -0
- package/src/cli/commands/project/close.ts +32 -0
- package/src/cli/commands/project/create.ts +93 -0
- package/src/cli/commands/project/list.ts +25 -0
- package/src/cli/commands/project/show.ts +32 -0
- package/src/cli/commands/{workspace → project}/switch.ts +19 -19
- package/src/cli/commands/tab/await.ts +2 -2
- package/src/cli/commands/tab/close.ts +3 -3
- package/src/cli/commands/tab/create.ts +74 -73
- package/src/cli/commands/tab/focus.ts +3 -3
- package/src/cli/commands/tab/list.ts +6 -6
- package/src/cli/commands/tab/run.ts +2 -2
- package/src/cli/commands/tab/send.ts +2 -2
- package/src/cli/commands/tab/snapshot.ts +2 -2
- package/src/cli/commands/tab/tail.ts +2 -2
- package/src/cli/commands/tab/wait.ts +2 -2
- package/src/cli/commands/worker/await.ts +3 -3
- package/src/cli/commands/worker/doctor.ts +30 -30
- package/src/cli/commands/worker/list.ts +15 -15
- package/src/cli/commands/worker/prompt.ts +3 -3
- package/src/cli/commands/worker/run.ts +23 -23
- package/src/cli/commands/worker/shared.ts +53 -59
- package/src/cli/commands/worker/stop.ts +29 -29
- package/src/cli/commands/worker/submit.ts +3 -3
- package/src/cli/commands/{worktree → workspace}/create-core.ts +26 -26
- package/src/cli/commands/workspace/create.ts +30 -68
- package/src/cli/commands/workspace/list.ts +52 -9
- package/src/cli/commands/workspace/remove.ts +81 -0
- package/src/cli/completion/plan.ts +2 -2
- package/src/cli/completion/sources.ts +18 -18
- package/src/cli/context.ts +13 -13
- package/src/cli/flags.ts +30 -10
- package/src/cli/index.ts +26 -16
- package/src/cli/output.ts +1 -1
- package/src/cli/registry.ts +12 -12
- package/src/config/loader.ts +16 -5
- package/src/config.ts +70 -112
- package/src/daemon/catalog-writer.ts +67 -67
- package/src/daemon/daemon.ts +205 -201
- package/src/daemon/session-manager.ts +39 -39
- package/src/daemon/session-registry.ts +14 -14
- package/src/git/divergence.ts +33 -2
- package/src/git/git-poller.ts +1 -1
- package/src/git/git-status.ts +2 -2
- package/src/git/{move-worktree.ts → move-workspace.ts} +5 -5
- package/src/git/pr-status.ts +1 -1
- package/src/git/repo-discovery.ts +1 -1
- package/src/git/use-repo-discovery.ts +1 -1
- package/src/git/workspace-branch-poller.ts +54 -0
- package/src/git/workspace-divergence-poller.ts +65 -0
- package/src/git/worktree.ts +29 -0
- package/src/index.tsx +4 -4
- package/src/input/keymap/help-entries.ts +7 -5
- package/src/input/modes/bridge.ts +13 -12
- package/src/input/modes/handlers/shared.ts +1 -1
- package/src/input/modes/transitions.ts +34 -21
- package/src/input/modes/types.ts +48 -31
- package/src/ipc/manager-protocol.ts +44 -44
- package/src/ipc/protocol.ts +192 -166
- package/src/platform/play-sound.ts +184 -0
- package/src/platform/project-search.ts +8 -8
- package/src/platform/worktree-paths.ts +8 -6
- package/src/pty/assistant-question-extractor.ts +1 -1
- package/src/pty/assistant-status-detection-loop.ts +79 -55
- package/src/pty/assistant-status-detector.ts +40 -15
- package/src/pty/command-registry.ts +81 -1
- package/src/restart-terminal-manager.ts +1 -1
- package/src/services/ai-usage/provider.ts +9 -2
- package/src/services/ai-usage/spawn.ts +19 -6
- package/src/session-backend/bootstrap.ts +4 -4
- package/src/session-backend/local-session-backend.ts +78 -75
- package/src/session-backend/remote-session-backend.ts +64 -52
- package/src/session-backend/types.ts +43 -32
- package/src/settings/live.ts +90 -0
- package/src/settings/search.ts +43 -0
- package/src/settings/sections/about.ts +49 -0
- package/src/settings/sections/appearance.ts +54 -0
- package/src/settings/sections/automation.ts +105 -0
- package/src/settings/sections/commands.ts +72 -0
- package/src/settings/sections/editor.ts +56 -0
- package/src/settings/sections/experimental.ts +57 -0
- package/src/settings/sections/git.ts +112 -0
- package/src/settings/sections/index.ts +95 -0
- package/src/settings/sections/integrations.ts +23 -0
- package/src/settings/sections/layout.ts +72 -0
- package/src/settings/sections/notifications.ts +99 -0
- package/src/settings/sections/setup.ts +54 -0
- package/src/settings/sections/status-bar.ts +63 -0
- package/src/settings/settings-store.ts +207 -0
- package/src/settings/types.ts +108 -0
- package/src/snippets/run-shell-var.ts +48 -22
- package/src/state/actions.ts +342 -0
- package/src/state/app-store.ts +2 -1
- package/src/state/bars.ts +1 -1
- package/src/state/dispatch-ref.ts +1 -1
- package/src/state/layout-resize.ts +2 -2
- package/src/state/project-catalog.ts +275 -0
- package/src/state/project-data.ts +143 -0
- package/src/state/{session-persistence.ts → project-persistence.ts} +74 -68
- package/src/state/project-save.ts +46 -0
- package/src/state/project-workspaces.ts +392 -0
- package/src/state/reducers/auto-commit-state.ts +11 -10
- package/src/state/reducers/git-commit-modal-state.ts +122 -0
- package/src/state/reducers/git-mode-state.ts +1 -1
- package/src/state/reducers/git-panel-state.ts +10 -6
- package/src/state/reducers/modal-state.ts +290 -681
- package/src/state/reducers/multi-repo-state.ts +3 -6
- package/src/state/reducers/project-state.ts +312 -0
- package/src/state/reducers/settings-state.ts +80 -0
- package/src/state/reducers/tab-state.ts +107 -60
- package/src/state/reducers/ui-state.ts +4 -3
- package/src/state/selectors.ts +49 -37
- package/src/state/store.ts +57 -24
- package/src/state/types.ts +244 -405
- package/src/state/validation.ts +14 -12
- package/src/terminal-manager/manager-client.ts +32 -32
- package/src/terminal-manager/terminal-manager.ts +24 -24
- package/src/ui/components/git/git-panel.tsx +2 -2
- package/src/ui/components/git/git-view.tsx +12 -12
- package/src/ui/components/git/image-diff/image-diff-view.tsx +1 -1
- package/src/ui/components/git/pane/git-pane-widget.tsx +7 -7
- package/src/ui/components/layout/sidebar/project-list.tsx +444 -0
- package/src/ui/components/layout/sidebar/tab-item.tsx +27 -12
- package/src/ui/components/layout/sidebar/use-sidebar-auto-scroll.ts +1 -1
- package/src/ui/components/layout/sidebar/workspace-row.tsx +253 -0
- package/src/ui/components/layout/status-bar.tsx +6 -2
- package/src/ui/components/layout/terminal-pane.tsx +34 -29
- package/src/ui/components/layout/top-tab-bar.tsx +46 -36
- package/src/ui/components/modals/git/git-commit-modal.tsx +8 -8
- package/src/ui/components/modals/{sessions/create-session-modal.tsx → projects/create-project-modal.tsx} +12 -12
- package/src/ui/components/modals/{sessions/session-name-modal.tsx → projects/project-name-modal.tsx} +2 -2
- package/src/ui/components/modals/{sessions/session-picker-modal.tsx → projects/project-picker-modal.tsx} +33 -35
- package/src/ui/components/modals/settings/settings-search-modal.tsx +70 -0
- package/src/ui/components/modals/shared/form.tsx +34 -22
- package/src/ui/components/modals/shared/picker.tsx +10 -10
- package/src/ui/components/modals/shared/{worktree-delete-confirm.tsx → workspace-delete-confirm.tsx} +9 -9
- package/src/ui/components/modals/tabs/new-tab-modal.tsx +30 -324
- package/src/ui/components/modals/workspace/create-workspace-modal.tsx +101 -0
- package/src/ui/components/modals/{worktree/worktree-move-confirm-modal.tsx → workspace/workspace-move-confirm-modal.tsx} +5 -5
- package/src/ui/components/modals/{worktree/worktree-move-modal.tsx → workspace/workspace-move-modal.tsx} +29 -27
- package/src/ui/components/primitives/input-field.tsx +7 -3
- package/src/ui/components/primitives/surface.tsx +3 -0
- package/src/ui/components/settings/row-value.tsx +74 -0
- package/src/ui/components/settings/settings-row.tsx +78 -0
- package/src/ui/components/settings/settings-view.tsx +169 -0
- package/src/ui/components/setup/setup-widget.tsx +152 -0
- package/src/ui/flash/build-labels.ts +27 -27
- package/src/ui/hooks/use-activity-sprite.ts +73 -0
- package/src/ui/hooks/use-scroll-active-into-view.ts +26 -0
- package/src/ui/{session-ordering.ts → project-ordering.ts} +4 -4
- package/src/ui/root.tsx +139 -121
- package/src/ui/status-bar-model.ts +48 -34
- package/src/ui/terminal-graphics/kitty.ts +28 -2
- package/src/ui/terminal-graphics/sprites/done@330/0.png +0 -0
- package/src/ui/terminal-graphics/sprites/done@330/1.png +0 -0
- package/src/ui/terminal-graphics/sprites/idle@1000/0.png +0 -0
- package/src/ui/terminal-graphics/sprites/idle@1000/1.png +0 -0
- package/src/ui/terminal-graphics/sprites/waiting@170/0.png +0 -0
- package/src/ui/terminal-graphics/sprites/waiting@170/1.png +0 -0
- package/src/ui/terminal-graphics/sprites/working@150/0.png +0 -0
- package/src/ui/terminal-graphics/sprites/working@150/1.png +0 -0
- package/src/ui/terminal-graphics/sprites/working@150/2.png +0 -0
- package/src/ui/terminal-graphics/sprites/working@150/3.png +0 -0
- package/src/ui/terminal-graphics/sprites.ts +246 -0
- package/src/ui/truncate.ts +7 -0
- package/src/ui/widgets/registry.tsx +6 -3
- package/src/ui/widgets/widget-context-menu.ts +26 -10
- package/src/update.ts +2 -2
- package/src/app-runtime/session-actions.ts +0 -187
- package/src/cli/client/workspace-resolver.ts +0 -110
- package/src/cli/commands/workspace/close.ts +0 -32
- package/src/cli/commands/workspace/show.ts +0 -32
- package/src/cli/commands/worktree/create.ts +0 -55
- package/src/cli/commands/worktree/list.ts +0 -68
- package/src/cli/commands/worktree/remove.ts +0 -81
- package/src/git/worktree-branch-poller.ts +0 -54
- package/src/git/worktree-divergence-poller.ts +0 -59
- package/src/state/reducers/session-state.ts +0 -262
- package/src/state/session-catalog.ts +0 -143
- package/src/state/session-worktrees.ts +0 -260
- package/src/state/workspace-save.ts +0 -43
- package/src/ui/components/layout/sidebar/use-top-tab-bar-auto-scroll.ts +0 -30
- package/src/ui/components/layout/sidebar/workspace-list.tsx +0 -418
- package/src/ui/components/layout/sidebar/worktree-row.tsx +0 -149
- /package/src/ui/{components/git/image-diff → terminal-graphics}/dimensions.ts +0 -0
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
IPC_CAPABILITY_THIN_ATTACH,
|
|
12
12
|
IPC_CAPABILITY_TURN_LIFECYCLE,
|
|
13
13
|
IPC_CAPABILITY_WORKER_METADATA,
|
|
14
|
-
|
|
14
|
+
IPC_CAPABILITY_WORKSPACE_LIFECYCLE_EVENTS,
|
|
15
15
|
} from '../../../ipc/protocol'
|
|
16
16
|
import {
|
|
17
17
|
getAllAssistantOptions,
|
|
@@ -19,10 +19,10 @@ import {
|
|
|
19
19
|
parseCommand,
|
|
20
20
|
} from '../../../pty/command-registry'
|
|
21
21
|
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
} from '../../client/
|
|
22
|
+
findPrimaryWorkspace,
|
|
23
|
+
PROJECT_ENV_VAR,
|
|
24
|
+
projectRepoRoot,
|
|
25
|
+
} from '../../client/project-resolver'
|
|
26
26
|
import { SHARED_FLAGS } from '../../flags'
|
|
27
27
|
import { EXIT_OK, EXIT_RUNTIME, writeJson } from '../../output'
|
|
28
28
|
import { WORKER_SCHEMA_VERSION } from './shared'
|
|
@@ -33,7 +33,7 @@ const REQUIRED_DAEMON_CAPABILITIES = [
|
|
|
33
33
|
IPC_CAPABILITY_THIN_ATTACH,
|
|
34
34
|
IPC_CAPABILITY_TURN_LIFECYCLE,
|
|
35
35
|
IPC_CAPABILITY_WORKER_METADATA,
|
|
36
|
-
|
|
36
|
+
IPC_CAPABILITY_WORKSPACE_LIFECYCLE_EVENTS,
|
|
37
37
|
] as const
|
|
38
38
|
|
|
39
39
|
export const workerDoctor: CliCommand = {
|
|
@@ -42,7 +42,7 @@ export const workerDoctor: CliCommand = {
|
|
|
42
42
|
group: 'worker',
|
|
43
43
|
run: async (ctx) => {
|
|
44
44
|
const daemon = await ctx.getDaemon()
|
|
45
|
-
const
|
|
45
|
+
const project = ctx.getProject()
|
|
46
46
|
const { version } = await import('../../../../package.json')
|
|
47
47
|
const { customCommands } = loadConfig()
|
|
48
48
|
const assistants = getAllAssistantOptions(customCommands).map((assistant) => ({
|
|
@@ -61,8 +61,8 @@ export const workerDoctor: CliCommand = {
|
|
|
61
61
|
const missingManagerCapabilities = [MANAGER_CAPABILITY_WORKER_METADATA].filter(
|
|
62
62
|
(capability) => !managerCapabilities.includes(capability)
|
|
63
63
|
)
|
|
64
|
-
const
|
|
65
|
-
const
|
|
64
|
+
const primaryWorkspace = findPrimaryWorkspace(project)
|
|
65
|
+
const projectOrigin = ctx.getProjectOrigin?.() ?? 'active'
|
|
66
66
|
const availableAssistants = assistants.filter((assistant) => assistant.available)
|
|
67
67
|
const skillPath = fileURLToPath(
|
|
68
68
|
new URL('../../../../skills/aimux-orchestrator/', import.meta.url)
|
|
@@ -78,8 +78,8 @@ export const workerDoctor: CliCommand = {
|
|
|
78
78
|
`restart the terminal manager; it is missing: ${missingManagerCapabilities.join(', ')}`
|
|
79
79
|
)
|
|
80
80
|
}
|
|
81
|
-
if (
|
|
82
|
-
issues.push('
|
|
81
|
+
if (primaryWorkspace === undefined) {
|
|
82
|
+
issues.push('project has no primary workspace; default isolated worker runs are unavailable')
|
|
83
83
|
}
|
|
84
84
|
if (availableAssistants.length === 0) {
|
|
85
85
|
issues.push('no configured assistant executable is available on PATH')
|
|
@@ -87,14 +87,14 @@ export const workerDoctor: CliCommand = {
|
|
|
87
87
|
if (!existsSync(skillPath)) {
|
|
88
88
|
issues.push(`packaged orchestrator skill is missing: ${skillPath}`)
|
|
89
89
|
}
|
|
90
|
-
// Not an issue — an inferred
|
|
90
|
+
// Not an issue — an inferred project is the normal interactive case — but
|
|
91
91
|
// it IS the one resolution mode that can follow the UI to another project
|
|
92
92
|
// between two calls. An orchestrator dispatching a multi-hour fleet wants to
|
|
93
93
|
// see this before it starts, not after it reviews diffs from the wrong repo.
|
|
94
94
|
const warnings: string[] = []
|
|
95
|
-
if (
|
|
95
|
+
if (projectOrigin === 'active') {
|
|
96
96
|
warnings.push(
|
|
97
|
-
`
|
|
97
|
+
`project "${project.name}" was inferred from the most recently opened project and follows the UI; pin it with --project or ${PROJECT_ENV_VAR}`
|
|
98
98
|
)
|
|
99
99
|
}
|
|
100
100
|
const ready = issues.length === 0
|
|
@@ -113,16 +113,16 @@ export const workerDoctor: CliCommand = {
|
|
|
113
113
|
missing: missingManagerCapabilities,
|
|
114
114
|
ok: missingManagerCapabilities.length === 0,
|
|
115
115
|
},
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
repoRoot: workspaceRepoRoot(workspace),
|
|
116
|
+
project: {
|
|
117
|
+
hasPrimaryWorkspace: primaryWorkspace !== undefined,
|
|
118
|
+
name: project.name,
|
|
119
|
+
ok: primaryWorkspace !== undefined,
|
|
120
|
+
/** Repo every fresh worker workspace is cut from — confirm before dispatching. */
|
|
121
|
+
repoRoot: projectRepoRoot(project),
|
|
123
122
|
/** 'flag' | 'env' | 'active'; only 'active' can follow the UI. */
|
|
124
|
-
source:
|
|
123
|
+
source: projectOrigin,
|
|
125
124
|
},
|
|
125
|
+
skill: { ok: existsSync(skillPath), path: skillPath },
|
|
126
126
|
},
|
|
127
127
|
cliVersion: version,
|
|
128
128
|
daemon: {
|
|
@@ -134,20 +134,20 @@ export const workerDoctor: CliCommand = {
|
|
|
134
134
|
protocolVersion: daemon.getSelectedVersion(),
|
|
135
135
|
},
|
|
136
136
|
issues,
|
|
137
|
+
project: {
|
|
138
|
+
id: project.id,
|
|
139
|
+
name: project.name,
|
|
140
|
+
projectPath: project.projectPath ?? null,
|
|
141
|
+
repoRoot: projectRepoRoot(project),
|
|
142
|
+
source: projectOrigin,
|
|
143
|
+
},
|
|
137
144
|
ready,
|
|
138
145
|
schemaVersion: WORKER_SCHEMA_VERSION,
|
|
139
146
|
skillPath,
|
|
140
147
|
warnings,
|
|
141
|
-
workspace: {
|
|
142
|
-
id: workspace.id,
|
|
143
|
-
name: workspace.name,
|
|
144
|
-
projectPath: workspace.projectPath ?? null,
|
|
145
|
-
repoRoot: workspaceRepoRoot(workspace),
|
|
146
|
-
source: workspaceOrigin,
|
|
147
|
-
},
|
|
148
148
|
})
|
|
149
149
|
return ready ? EXIT_OK : EXIT_RUNTIME
|
|
150
150
|
},
|
|
151
|
-
summary: 'Check worker prerequisites, versions, assistants, and
|
|
151
|
+
summary: 'Check worker prerequisites, versions, assistants, and project',
|
|
152
152
|
verb: 'doctor',
|
|
153
153
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CliCommand } from '../../registry'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { projectIdentity } from '../../client/project-resolver'
|
|
4
4
|
import { SHARED_FLAGS } from '../../flags'
|
|
5
5
|
import { EXIT_OK, writeJson } from '../../output'
|
|
6
6
|
import {
|
|
@@ -16,9 +16,9 @@ export const workerList: CliCommand = {
|
|
|
16
16
|
flags: [
|
|
17
17
|
...SHARED_FLAGS,
|
|
18
18
|
{
|
|
19
|
-
description: 'list workers in every catalogued
|
|
19
|
+
description: 'list workers in every catalogued project, not just the target one',
|
|
20
20
|
kind: 'boolean',
|
|
21
|
-
name: 'all-
|
|
21
|
+
name: 'all-projects',
|
|
22
22
|
},
|
|
23
23
|
],
|
|
24
24
|
group: 'worker',
|
|
@@ -27,41 +27,41 @@ export const workerList: CliCommand = {
|
|
|
27
27
|
|
|
28
28
|
// `{"workers":[]}` alone is indistinguishable from "every worker died", and
|
|
29
29
|
// the natural recovery from that reading is a destructive re-dispatch. Name
|
|
30
|
-
// the
|
|
30
|
+
// the project that was queried so an empty fleet is legible as "not here"
|
|
31
31
|
// rather than "gone", and offer one call that answers "are they really gone?"
|
|
32
|
-
if (ctx.args.flags['all-
|
|
32
|
+
if (ctx.args.flags['all-projects'] === true) {
|
|
33
33
|
const targets = await listWorkerTargets(ctx)
|
|
34
34
|
writeJson({
|
|
35
35
|
schemaVersion: WORKER_SCHEMA_VERSION,
|
|
36
36
|
workers: targets
|
|
37
37
|
.filter((target) => selector === undefined || target.tab.workerName === selector)
|
|
38
38
|
.map((target) => ({
|
|
39
|
-
...workerView(target.
|
|
40
|
-
|
|
39
|
+
...workerView(target.project, target.tab),
|
|
40
|
+
project: projectIdentity(target.project),
|
|
41
41
|
})),
|
|
42
42
|
})
|
|
43
43
|
return EXIT_OK
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
if (selector !== undefined) {
|
|
47
|
-
const {
|
|
47
|
+
const { project, tab } = await resolveWorkerTarget(ctx, selector)
|
|
48
48
|
writeJson({
|
|
49
|
+
project: projectIdentity(project),
|
|
49
50
|
schemaVersion: WORKER_SCHEMA_VERSION,
|
|
50
|
-
workers: [workerView(
|
|
51
|
-
workspace: workspaceIdentity(workspace),
|
|
51
|
+
workers: [workerView(project, tab)],
|
|
52
52
|
})
|
|
53
53
|
return EXIT_OK
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
const
|
|
57
|
-
const tabs = await listNamedWorkerTabs(ctx,
|
|
56
|
+
const project = ctx.getProject()
|
|
57
|
+
const tabs = await listNamedWorkerTabs(ctx, project)
|
|
58
58
|
writeJson({
|
|
59
|
+
project: projectIdentity(project),
|
|
59
60
|
schemaVersion: WORKER_SCHEMA_VERSION,
|
|
60
|
-
workers: tabs.map((tab) => workerView(
|
|
61
|
-
workspace: workspaceIdentity(workspace),
|
|
61
|
+
workers: tabs.map((tab) => workerView(project, tab)),
|
|
62
62
|
})
|
|
63
63
|
return EXIT_OK
|
|
64
64
|
},
|
|
65
|
-
summary: 'List named workers with liveness and
|
|
65
|
+
summary: 'List named workers with liveness and workspace context',
|
|
66
66
|
verb: 'list',
|
|
67
67
|
}
|
|
@@ -48,7 +48,7 @@ export const workerPrompt: CliCommand = {
|
|
|
48
48
|
group: 'worker',
|
|
49
49
|
run: async (ctx) => {
|
|
50
50
|
const selector = ctx.args.positionals[0] ?? ''
|
|
51
|
-
const {
|
|
51
|
+
const { project, tab } = await resolveWorkerTarget(ctx, selector)
|
|
52
52
|
const promptFile =
|
|
53
53
|
typeof ctx.args.flags['prompt-file'] === 'string' ? ctx.args.flags['prompt-file'] : undefined
|
|
54
54
|
const text = await resolvePromptText(
|
|
@@ -56,7 +56,7 @@ export const workerPrompt: CliCommand = {
|
|
|
56
56
|
ctx.args.flags.stdin === true,
|
|
57
57
|
ctx.args.positionals[1]
|
|
58
58
|
)
|
|
59
|
-
const outcome = await dispatchWorkerPrompt(ctx,
|
|
59
|
+
const outcome = await dispatchWorkerPrompt(ctx, project, tab.id, text, {
|
|
60
60
|
detach: ctx.args.flags.detach === true,
|
|
61
61
|
replace: ctx.args.flags.replace === true,
|
|
62
62
|
timeoutMs:
|
|
@@ -66,7 +66,7 @@ export const workerPrompt: CliCommand = {
|
|
|
66
66
|
? ctx.args.flags['uptake-timeout']
|
|
67
67
|
: undefined,
|
|
68
68
|
})
|
|
69
|
-
writeJson(workerEnvelope(
|
|
69
|
+
writeJson(workerEnvelope(project, workerView(project, tab), outcome))
|
|
70
70
|
return workerOutcomeExitCode(outcome)
|
|
71
71
|
},
|
|
72
72
|
summary: 'Prompt an existing named worker and await its outcome',
|
|
@@ -19,7 +19,7 @@ export const workerRun: CliCommand = {
|
|
|
19
19
|
...SHARED_FLAGS,
|
|
20
20
|
{
|
|
21
21
|
complete: { kind: 'none' },
|
|
22
|
-
description: 'unique
|
|
22
|
+
description: 'unique project-scoped worker name',
|
|
23
23
|
kind: 'string',
|
|
24
24
|
name: 'name',
|
|
25
25
|
},
|
|
@@ -61,25 +61,25 @@ export const workerRun: CliCommand = {
|
|
|
61
61
|
name: 'uptake-timeout',
|
|
62
62
|
},
|
|
63
63
|
{
|
|
64
|
-
complete: { kind: 'dynamic', source: '
|
|
65
|
-
description: 'co-locate in an existing
|
|
64
|
+
complete: { kind: 'dynamic', source: 'workspace' },
|
|
65
|
+
description: 'co-locate in an existing workspace id',
|
|
66
66
|
kind: 'string',
|
|
67
|
-
name: '
|
|
67
|
+
name: 'workspace',
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
|
-
description: 'run in the
|
|
70
|
+
description: 'run in the project active workspace instead of creating one',
|
|
71
71
|
kind: 'boolean',
|
|
72
|
-
name: 'no-
|
|
72
|
+
name: 'no-workspace',
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
complete: { kind: 'dynamic', source: 'git-ref' },
|
|
76
|
-
description: 'base ref for the fresh
|
|
76
|
+
description: 'base ref for the fresh workspace (default HEAD)',
|
|
77
77
|
kind: 'string',
|
|
78
78
|
name: 'base',
|
|
79
79
|
},
|
|
80
80
|
{
|
|
81
81
|
complete: { kind: 'none' },
|
|
82
|
-
description: 'branch for the fresh
|
|
82
|
+
description: 'branch for the fresh workspace (default aimux/<name>)',
|
|
83
83
|
kind: 'string',
|
|
84
84
|
name: 'branch',
|
|
85
85
|
},
|
|
@@ -90,11 +90,11 @@ export const workerRun: CliCommand = {
|
|
|
90
90
|
const assistant = typeof ctx.args.flags.assistant === 'string' ? ctx.args.flags.assistant : ''
|
|
91
91
|
validateWorkerName(name)
|
|
92
92
|
if (assistant === '') throw new CliUsageError('--assistant is required')
|
|
93
|
-
const
|
|
94
|
-
typeof ctx.args.flags.
|
|
95
|
-
const
|
|
96
|
-
if (
|
|
97
|
-
throw new CliUsageError('--
|
|
93
|
+
const workspace =
|
|
94
|
+
typeof ctx.args.flags.workspace === 'string' ? ctx.args.flags.workspace : undefined
|
|
95
|
+
const noWorkspace = ctx.args.flags['no-workspace'] === true
|
|
96
|
+
if (workspace !== undefined && noWorkspace) {
|
|
97
|
+
throw new CliUsageError('--workspace and --no-workspace are mutually exclusive')
|
|
98
98
|
}
|
|
99
99
|
const promptFile =
|
|
100
100
|
typeof ctx.args.flags['prompt-file'] === 'string' ? ctx.args.flags['prompt-file'] : undefined
|
|
@@ -103,27 +103,27 @@ export const workerRun: CliCommand = {
|
|
|
103
103
|
ctx.args.flags.stdin === true,
|
|
104
104
|
ctx.args.positionals[0]
|
|
105
105
|
)
|
|
106
|
-
// Resolve the target
|
|
107
|
-
// every later step. The repo a
|
|
108
|
-
// so an orchestrator that omits --
|
|
106
|
+
// Resolve the target project ONCE, up front, and use that record for
|
|
107
|
+
// every later step. The repo a workspace is cut from comes from this record,
|
|
108
|
+
// so an orchestrator that omits --project at least gets the resolved
|
|
109
109
|
// identity echoed back in the response instead of having to infer it.
|
|
110
|
-
const
|
|
110
|
+
const project = ctx.getProject()
|
|
111
111
|
const result = await createCliTab(ctx, {
|
|
112
112
|
assistantId: assistant,
|
|
113
113
|
base: typeof ctx.args.flags.base === 'string' ? ctx.args.flags.base : undefined,
|
|
114
114
|
branch: typeof ctx.args.flags.branch === 'string' ? ctx.args.flags.branch : undefined,
|
|
115
115
|
effort: typeof ctx.args.flags.effort === 'string' ? ctx.args.flags.effort : undefined,
|
|
116
116
|
model: typeof ctx.args.flags.model === 'string' ? ctx.args.flags.model : undefined,
|
|
117
|
-
|
|
117
|
+
newWorkspace: noWorkspace || workspace !== undefined ? undefined : name,
|
|
118
118
|
title: name,
|
|
119
119
|
workerName: name,
|
|
120
|
-
|
|
120
|
+
workspaceId: workspace,
|
|
121
121
|
})
|
|
122
|
-
const tabs = await (await ctx.getDaemon()).listTabs(
|
|
122
|
+
const tabs = await (await ctx.getDaemon()).listTabs(project.id)
|
|
123
123
|
const tab = tabs.tabs.find((entry) => entry.id === result.tabId)
|
|
124
124
|
if (!tab) throw new Error(`created worker disappeared: ${result.tabId}`)
|
|
125
|
-
const worker = workerView(
|
|
126
|
-
const outcome = await dispatchWorkerPrompt(ctx,
|
|
125
|
+
const worker = workerView(project, tab)
|
|
126
|
+
const outcome = await dispatchWorkerPrompt(ctx, project, result.tabId, text, {
|
|
127
127
|
// The tab was spawned microseconds ago: its assistant is still booting, so
|
|
128
128
|
// the prompt must not be written until the TUI is reading keystrokes.
|
|
129
129
|
awaitFirstPaint: true,
|
|
@@ -135,7 +135,7 @@ export const workerRun: CliCommand = {
|
|
|
135
135
|
? ctx.args.flags['uptake-timeout']
|
|
136
136
|
: undefined,
|
|
137
137
|
})
|
|
138
|
-
writeJson(workerEnvelope(
|
|
138
|
+
writeJson(workerEnvelope(project, worker, outcome))
|
|
139
139
|
return workerOutcomeExitCode(outcome)
|
|
140
140
|
},
|
|
141
141
|
summary: 'Create a named worker, dispatch a prompt, and await its outcome',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ProjectRecord, TerminalSnapshot, WorkspaceRecord } from '../../../state/types'
|
|
2
2
|
import type { CliContext } from '../../context'
|
|
3
3
|
|
|
4
4
|
import {
|
|
@@ -9,11 +9,7 @@ import {
|
|
|
9
9
|
IPC_CAPABILITY_WORKER_METADATA,
|
|
10
10
|
type TabSessionSummary,
|
|
11
11
|
} from '../../../ipc/protocol'
|
|
12
|
-
import {
|
|
13
|
-
listWorkspaces,
|
|
14
|
-
workspaceIdentity,
|
|
15
|
-
workspaceRepoRoot,
|
|
16
|
-
} from '../../client/workspace-resolver'
|
|
12
|
+
import { listProjects, projectIdentity, projectRepoRoot } from '../../client/project-resolver'
|
|
17
13
|
import { CliUsageError } from '../../flags'
|
|
18
14
|
import {
|
|
19
15
|
EXIT_OK,
|
|
@@ -62,12 +58,12 @@ export interface WorkerView {
|
|
|
62
58
|
lastLine?: string
|
|
63
59
|
name: string
|
|
64
60
|
path: string | null
|
|
65
|
-
/** Repository the worker's
|
|
61
|
+
/** Repository the worker's workspace was cut from — see `projectRepoRoot`. */
|
|
66
62
|
repoRoot: string | null
|
|
67
63
|
status: TabSessionSummary['status']
|
|
68
64
|
tabId: string
|
|
69
65
|
title: string
|
|
70
|
-
|
|
66
|
+
workspaceId: string | null
|
|
71
67
|
}
|
|
72
68
|
|
|
73
69
|
export interface WorkerOutcome {
|
|
@@ -85,10 +81,10 @@ export interface WorkerOutcome {
|
|
|
85
81
|
}
|
|
86
82
|
}
|
|
87
83
|
|
|
88
|
-
/** A worker plus the
|
|
84
|
+
/** A worker plus the project it is actually bound to. */
|
|
89
85
|
export interface WorkerTarget {
|
|
90
86
|
tab: TabSessionSummary
|
|
91
|
-
|
|
87
|
+
project: ProjectRecord
|
|
92
88
|
}
|
|
93
89
|
|
|
94
90
|
export function validateWorkerName(name: string): void {
|
|
@@ -99,32 +95,32 @@ export function validateWorkerName(name: string): void {
|
|
|
99
95
|
}
|
|
100
96
|
}
|
|
101
97
|
|
|
102
|
-
function
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
):
|
|
106
|
-
if (
|
|
107
|
-
return
|
|
98
|
+
function workspaceFor(
|
|
99
|
+
project: ProjectRecord,
|
|
100
|
+
workspaceId: string | undefined
|
|
101
|
+
): WorkspaceRecord | undefined {
|
|
102
|
+
if (workspaceId === undefined) return undefined
|
|
103
|
+
return project.workspaces?.find((workspace) => workspace.id === workspaceId)
|
|
108
104
|
}
|
|
109
105
|
|
|
110
|
-
export function workerView(
|
|
106
|
+
export function workerView(project: ProjectRecord, tab: TabSessionSummary): WorkerView {
|
|
111
107
|
if (tab.workerName === undefined) {
|
|
112
108
|
throw new Error(`tab is not a named worker: ${tab.id}`)
|
|
113
109
|
}
|
|
114
|
-
const
|
|
110
|
+
const workspace = workspaceFor(project, tab.workspaceId)
|
|
115
111
|
return {
|
|
116
112
|
activity: tab.activity,
|
|
117
113
|
assistant: tab.assistant,
|
|
118
|
-
branch:
|
|
114
|
+
branch: workspace?.branch ?? null,
|
|
119
115
|
command: tab.command,
|
|
120
116
|
lastLine: tab.lastLine,
|
|
121
117
|
name: tab.workerName,
|
|
122
|
-
path:
|
|
123
|
-
repoRoot:
|
|
118
|
+
path: workspace?.path ?? null,
|
|
119
|
+
repoRoot: workspace?.repoRoot ?? projectRepoRoot(project),
|
|
124
120
|
status: tab.status,
|
|
125
121
|
tabId: tab.id,
|
|
126
122
|
title: tab.title,
|
|
127
|
-
|
|
123
|
+
workspaceId: tab.workspaceId ?? null,
|
|
128
124
|
}
|
|
129
125
|
}
|
|
130
126
|
|
|
@@ -143,20 +139,20 @@ async function requireWorkerCapabilities(
|
|
|
143
139
|
|
|
144
140
|
export async function listNamedWorkerTabs(
|
|
145
141
|
ctx: CliContext,
|
|
146
|
-
|
|
142
|
+
project: ProjectRecord = ctx.getProject()
|
|
147
143
|
): Promise<TabSessionSummary[]> {
|
|
148
144
|
const daemon = await requireWorkerCapabilities(ctx)
|
|
149
|
-
return (await daemon.listTabs(
|
|
145
|
+
return (await daemon.listTabs(project.id)).tabs.filter((tab) => tab.workerName !== undefined)
|
|
150
146
|
}
|
|
151
147
|
|
|
152
|
-
/** Every named worker the daemon knows about, across every catalogued
|
|
148
|
+
/** Every named worker the daemon knows about, across every catalogued project. */
|
|
153
149
|
export async function listWorkerTargets(ctx: CliContext): Promise<WorkerTarget[]> {
|
|
154
150
|
const daemon = await requireWorkerCapabilities(ctx)
|
|
155
151
|
const targets: WorkerTarget[] = []
|
|
156
|
-
for (const
|
|
157
|
-
const { tabs } = await daemon.listTabs(
|
|
152
|
+
for (const project of ctx.getProjects?.() ?? listProjects()) {
|
|
153
|
+
const { tabs } = await daemon.listTabs(project.id)
|
|
158
154
|
for (const tab of tabs) {
|
|
159
|
-
if (tab.workerName !== undefined) targets.push({
|
|
155
|
+
if (tab.workerName !== undefined) targets.push({ project, tab })
|
|
160
156
|
}
|
|
161
157
|
}
|
|
162
158
|
return targets
|
|
@@ -167,47 +163,45 @@ function matches(tab: TabSessionSummary, selector: string): boolean {
|
|
|
167
163
|
}
|
|
168
164
|
|
|
169
165
|
/**
|
|
170
|
-
* Resolve a worker selector to the tab AND the
|
|
166
|
+
* Resolve a worker selector to the tab AND the project that owns it.
|
|
171
167
|
*
|
|
172
|
-
* A worker is addressed for its whole lifetime, but the *active*
|
|
168
|
+
* A worker is addressed for its whole lifetime, but the *active* project can
|
|
173
169
|
* change under it (the UI switching projects is enough). Resolving only against
|
|
174
|
-
* the active
|
|
170
|
+
* the active project is what made a live fleet look dead — `worker list`
|
|
175
171
|
* returning `[]` and `worker await` saying "worker not found" while all twelve
|
|
176
|
-
* workers were healthy. So when the
|
|
172
|
+
* workers were healthy. So when the project was merely inferred, fall back to
|
|
177
173
|
* a catalog-wide search and bind to wherever the worker actually lives; when it
|
|
178
|
-
* was pinned (`--
|
|
179
|
-
*
|
|
174
|
+
* was pinned (`--project` / `AIMUX_PROJECT`), keep the boundary but name the
|
|
175
|
+
* project that does hold it instead of a bare "not found".
|
|
180
176
|
*/
|
|
181
177
|
export async function resolveWorkerTarget(
|
|
182
178
|
ctx: CliContext,
|
|
183
179
|
selector: string
|
|
184
180
|
): Promise<WorkerTarget> {
|
|
185
|
-
const
|
|
186
|
-
const local = (await listNamedWorkerTabs(ctx,
|
|
181
|
+
const project = ctx.getProject()
|
|
182
|
+
const local = (await listNamedWorkerTabs(ctx, project)).filter((tab) => matches(tab, selector))
|
|
187
183
|
if (local.length > 1) {
|
|
188
184
|
throw new Error(
|
|
189
|
-
`worker selector is ambiguous in
|
|
185
|
+
`worker selector is ambiguous in project "${project.name}": ${selector} (${local
|
|
190
186
|
.map((tab) => tab.id)
|
|
191
187
|
.join(', ')})`
|
|
192
188
|
)
|
|
193
189
|
}
|
|
194
190
|
const only = local[0]
|
|
195
|
-
if (only) return { tab: only
|
|
191
|
+
if (only) return { project, tab: only }
|
|
196
192
|
|
|
197
193
|
const elsewhere = (await listWorkerTargets(ctx)).filter(
|
|
198
|
-
(target) => target.
|
|
194
|
+
(target) => target.project.id !== project.id && matches(target.tab, selector)
|
|
199
195
|
)
|
|
200
|
-
const pinned = (ctx.
|
|
196
|
+
const pinned = (ctx.getProjectOrigin?.() ?? 'active') !== 'active'
|
|
201
197
|
if (elsewhere.length === 1 && elsewhere[0] && !pinned) return elsewhere[0]
|
|
202
198
|
if (elsewhere.length > 0) {
|
|
203
|
-
const where = elsewhere
|
|
204
|
-
.map((target) => `${target.workspace.name} (${target.tab.id})`)
|
|
205
|
-
.join(', ')
|
|
199
|
+
const where = elsewhere.map((target) => `${target.project.name} (${target.tab.id})`).join(', ')
|
|
206
200
|
throw new Error(
|
|
207
|
-
`worker not found in
|
|
201
|
+
`worker not found in project "${project.name}": ${selector} — it lives in ${where}; re-run with --project`
|
|
208
202
|
)
|
|
209
203
|
}
|
|
210
|
-
throw new Error(`worker not found in
|
|
204
|
+
throw new Error(`worker not found in project "${project.name}": ${selector}`)
|
|
211
205
|
}
|
|
212
206
|
|
|
213
207
|
export function normalizeTurnOutcome(outcome: TurnOutcome): WorkerOutcome {
|
|
@@ -331,11 +325,11 @@ async function armUptake(
|
|
|
331
325
|
*/
|
|
332
326
|
async function liveUptake(
|
|
333
327
|
daemon: Awaited<ReturnType<CliContext['getDaemon']>>,
|
|
334
|
-
|
|
328
|
+
projectId: string,
|
|
335
329
|
tabId: string,
|
|
336
330
|
startedAt: number
|
|
337
331
|
): Promise<UptakeResult | undefined> {
|
|
338
|
-
const live = (await daemon.listTabs(
|
|
332
|
+
const live = (await daemon.listTabs(projectId)).tabs.find((tab) => tab.id === tabId)
|
|
339
333
|
if (live?.activity !== 'working' && live?.activity !== 'waiting-input') return undefined
|
|
340
334
|
return { activity: live.activity, confirmed: true, ms: Date.now() - startedAt }
|
|
341
335
|
}
|
|
@@ -357,13 +351,13 @@ export interface DispatchOptions {
|
|
|
357
351
|
|
|
358
352
|
export async function dispatchWorkerPrompt(
|
|
359
353
|
ctx: CliContext,
|
|
360
|
-
|
|
354
|
+
project: ProjectRecord,
|
|
361
355
|
tabId: string,
|
|
362
356
|
text: string,
|
|
363
357
|
options: DispatchOptions
|
|
364
358
|
): Promise<WorkerOutcome> {
|
|
365
359
|
const { daemon } = await requireTurnCapabilities(ctx)
|
|
366
|
-
const attach = await daemon.attach({ cols: 0,
|
|
360
|
+
const attach = await daemon.attach({ cols: 0, projectId: project.id, rows: 0, thin: true })
|
|
367
361
|
const attached = attach.tabs.find((tab) => tab.id === tabId)
|
|
368
362
|
if (!attached) throw new Error(`tab not found: ${tabId}`)
|
|
369
363
|
const payload = buildPromptPayload(text, false)
|
|
@@ -397,7 +391,7 @@ export async function dispatchWorkerPrompt(
|
|
|
397
391
|
let resubmits = 0
|
|
398
392
|
|
|
399
393
|
while (!result.confirmed && resubmits < MAX_UPTAKE_RESUBMITS) {
|
|
400
|
-
const live = await liveUptake(daemon,
|
|
394
|
+
const live = await liveUptake(daemon, project.id, tabId, startedAt)
|
|
401
395
|
if (live) {
|
|
402
396
|
result = live
|
|
403
397
|
break
|
|
@@ -413,7 +407,7 @@ export async function dispatchWorkerPrompt(
|
|
|
413
407
|
// Last look before declaring failure: never report a healthy, working worker
|
|
414
408
|
// as pending just because an edge-triggered event was missed.
|
|
415
409
|
if (!result.confirmed) {
|
|
416
|
-
result = (await liveUptake(daemon,
|
|
410
|
+
result = (await liveUptake(daemon, project.id, tabId, startedAt)) ?? result
|
|
417
411
|
}
|
|
418
412
|
|
|
419
413
|
if (!result.confirmed) {
|
|
@@ -438,19 +432,19 @@ export async function dispatchWorkerPrompt(
|
|
|
438
432
|
*/
|
|
439
433
|
export async function submitWorkerPrompt(
|
|
440
434
|
ctx: CliContext,
|
|
441
|
-
|
|
435
|
+
project: ProjectRecord,
|
|
442
436
|
tabId: string,
|
|
443
437
|
uptakeTimeoutMs: number
|
|
444
438
|
): Promise<WorkerOutcome> {
|
|
445
439
|
const { daemon } = await requireTurnCapabilities(ctx)
|
|
446
|
-
const attach = await daemon.attach({ cols: 0,
|
|
440
|
+
const attach = await daemon.attach({ cols: 0, projectId: project.id, rows: 0, thin: true })
|
|
447
441
|
if (!attach.tabs.some((tab) => tab.id === tabId)) throw new Error(`tab not found: ${tabId}`)
|
|
448
442
|
const startedAt = Date.now()
|
|
449
443
|
const uptake = armUptake(daemon, tabId, uptakeTimeoutMs, startedAt)
|
|
450
444
|
await daemon.expectOk('write', { data: '\r', tabId })
|
|
451
445
|
const result = await uptake
|
|
452
446
|
if (!result.confirmed) {
|
|
453
|
-
const live = await liveUptake(daemon,
|
|
447
|
+
const live = await liveUptake(daemon, project.id, tabId, startedAt)
|
|
454
448
|
if (live) {
|
|
455
449
|
return { durationMs: Date.now() - startedAt, status: 'dispatched', uptake: live }
|
|
456
450
|
}
|
|
@@ -466,15 +460,15 @@ export async function submitWorkerPrompt(
|
|
|
466
460
|
|
|
467
461
|
export async function awaitExistingWorker(
|
|
468
462
|
ctx: CliContext,
|
|
469
|
-
|
|
463
|
+
project: ProjectRecord,
|
|
470
464
|
tabId: string,
|
|
471
465
|
timeoutMs: number
|
|
472
466
|
): Promise<WorkerOutcome> {
|
|
473
467
|
const { daemon } = await requireTurnCapabilities(ctx)
|
|
474
468
|
const attach = await daemon.attach({
|
|
475
469
|
cols: 0,
|
|
470
|
+
projectId: project.id,
|
|
476
471
|
rows: 0,
|
|
477
|
-
sessionId: workspace.id,
|
|
478
472
|
thin: true,
|
|
479
473
|
})
|
|
480
474
|
const tab = attach.tabs.find((entry) => entry.id === tabId)
|
|
@@ -497,19 +491,19 @@ export async function awaitExistingWorker(
|
|
|
497
491
|
}
|
|
498
492
|
|
|
499
493
|
/**
|
|
500
|
-
* Versioned envelope. `
|
|
494
|
+
* Versioned envelope. `project` is not decoration: without it a caller cannot
|
|
501
495
|
* tell which project a worker belongs to, which is how a whole orchestration
|
|
502
496
|
* can run against the wrong repository unnoticed.
|
|
503
497
|
*/
|
|
504
498
|
export function workerEnvelope(
|
|
505
|
-
|
|
499
|
+
project: ProjectRecord,
|
|
506
500
|
worker: WorkerView,
|
|
507
501
|
outcome?: WorkerOutcome
|
|
508
502
|
): Record<string, unknown> {
|
|
509
503
|
return {
|
|
504
|
+
project: projectIdentity(project),
|
|
510
505
|
schemaVersion: WORKER_SCHEMA_VERSION,
|
|
511
506
|
worker,
|
|
512
|
-
workspace: workspaceIdentity(workspace),
|
|
513
507
|
...(outcome === undefined ? {} : { outcome }),
|
|
514
508
|
}
|
|
515
509
|
}
|