macro-agent 0.1.8 → 0.1.10
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/CLAUDE.md +166 -33
- package/README.md +781 -131
- package/dist/acp/claude-code-replay.d.ts +11 -0
- package/dist/acp/claude-code-replay.d.ts.map +1 -0
- package/dist/acp/claude-code-replay.js +190 -0
- package/dist/acp/claude-code-replay.js.map +1 -0
- package/dist/acp/macro-agent.d.ts.map +1 -1
- package/dist/acp/macro-agent.js +155 -6
- package/dist/acp/macro-agent.js.map +1 -1
- package/dist/acp/types.d.ts +9 -0
- package/dist/acp/types.d.ts.map +1 -1
- package/dist/acp/types.js.map +1 -1
- package/dist/agent/agent-manager-v2.d.ts +21 -0
- package/dist/agent/agent-manager-v2.d.ts.map +1 -1
- package/dist/agent/agent-manager-v2.js +234 -43
- package/dist/agent/agent-manager-v2.js.map +1 -1
- package/dist/agent/agent-manager.d.ts +12 -0
- package/dist/agent/agent-manager.d.ts.map +1 -1
- package/dist/agent/agent-manager.js.map +1 -1
- package/dist/agent/types.d.ts +15 -2
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/boot-v2.d.ts +41 -0
- package/dist/boot-v2.d.ts.map +1 -1
- package/dist/boot-v2.js +16 -1
- package/dist/boot-v2.js.map +1 -1
- package/dist/cli/index.js +56 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cognitive/macro-agent-backend.d.ts.map +1 -1
- package/dist/cognitive/macro-agent-backend.js +40 -22
- package/dist/cognitive/macro-agent-backend.js.map +1 -1
- package/dist/integrations/skilltree.d.ts.map +1 -1
- package/dist/integrations/skilltree.js +1 -0
- package/dist/integrations/skilltree.js.map +1 -1
- package/dist/lifecycle/cleanup.d.ts +33 -2
- package/dist/lifecycle/cleanup.d.ts.map +1 -1
- package/dist/lifecycle/cleanup.js +28 -6
- package/dist/lifecycle/cleanup.js.map +1 -1
- package/dist/lifecycle/handlers-v2.d.ts +7 -0
- package/dist/lifecycle/handlers-v2.d.ts.map +1 -1
- package/dist/lifecycle/handlers-v2.js +28 -2
- package/dist/lifecycle/handlers-v2.js.map +1 -1
- package/dist/lifecycle/types.d.ts +11 -0
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/lifecycle/types.js.map +1 -1
- package/dist/map/acp-bridge.d.ts +9 -0
- package/dist/map/acp-bridge.d.ts.map +1 -1
- package/dist/map/acp-bridge.js +15 -2
- package/dist/map/acp-bridge.js.map +1 -1
- package/dist/map/cascade-bridge.d.ts +44 -0
- package/dist/map/cascade-bridge.d.ts.map +1 -0
- package/dist/map/cascade-bridge.js +257 -0
- package/dist/map/cascade-bridge.js.map +1 -0
- package/dist/map/lifecycle-bridge.d.ts +1 -1
- package/dist/map/lifecycle-bridge.d.ts.map +1 -1
- package/dist/map/lifecycle-bridge.js +58 -23
- package/dist/map/lifecycle-bridge.js.map +1 -1
- package/dist/map/server.d.ts.map +1 -1
- package/dist/map/server.js +47 -6
- package/dist/map/server.js.map +1 -1
- package/dist/map/sidecar.d.ts.map +1 -1
- package/dist/map/sidecar.js +33 -2
- package/dist/map/sidecar.js.map +1 -1
- package/dist/map/types.d.ts +20 -0
- package/dist/map/types.d.ts.map +1 -1
- package/dist/mcp/tools/done-v2.d.ts.map +1 -1
- package/dist/mcp/tools/done-v2.js +8 -0
- package/dist/mcp/tools/done-v2.js.map +1 -1
- package/dist/teams/team-manager-v2.d.ts.map +1 -1
- package/dist/teams/team-manager-v2.js +26 -0
- package/dist/teams/team-manager-v2.js.map +1 -1
- package/dist/teams/team-runtime-v2.d.ts.map +1 -1
- package/dist/teams/team-runtime-v2.js +16 -3
- package/dist/teams/team-runtime-v2.js.map +1 -1
- package/dist/workspace/config.d.ts +10 -10
- package/dist/workspace/config.d.ts.map +1 -1
- package/dist/workspace/config.js +4 -4
- package/dist/workspace/config.js.map +1 -1
- package/dist/workspace/git-cascade-adapter.d.ts +510 -0
- package/dist/workspace/git-cascade-adapter.d.ts.map +1 -0
- package/dist/workspace/git-cascade-adapter.js +908 -0
- package/dist/workspace/git-cascade-adapter.js.map +1 -0
- package/dist/workspace/index.d.ts +3 -3
- package/dist/workspace/index.d.ts.map +1 -1
- package/dist/workspace/index.js +4 -4
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/landing/direct-push.d.ts +20 -0
- package/dist/workspace/landing/direct-push.d.ts.map +1 -0
- package/dist/workspace/landing/direct-push.js +74 -0
- package/dist/workspace/landing/direct-push.js.map +1 -0
- package/dist/workspace/landing/index.d.ts +29 -0
- package/dist/workspace/landing/index.d.ts.map +1 -0
- package/dist/workspace/landing/index.js +37 -0
- package/dist/workspace/landing/index.js.map +1 -0
- package/dist/workspace/landing/merge-to-parent.d.ts +41 -0
- package/dist/workspace/landing/merge-to-parent.d.ts.map +1 -0
- package/dist/workspace/landing/merge-to-parent.js +185 -0
- package/dist/workspace/landing/merge-to-parent.js.map +1 -0
- package/dist/workspace/landing/optimistic-push.d.ts +16 -0
- package/dist/workspace/landing/optimistic-push.d.ts.map +1 -0
- package/dist/workspace/landing/optimistic-push.js +27 -0
- package/dist/workspace/landing/optimistic-push.js.map +1 -0
- package/dist/workspace/landing/queue-to-branch.d.ts +24 -0
- package/dist/workspace/landing/queue-to-branch.d.ts.map +1 -0
- package/dist/workspace/landing/queue-to-branch.js +79 -0
- package/dist/workspace/landing/queue-to-branch.js.map +1 -0
- package/dist/workspace/merge-queue/merge-queue.d.ts +10 -0
- package/dist/workspace/merge-queue/merge-queue.d.ts.map +1 -1
- package/dist/workspace/merge-queue/merge-queue.js +10 -0
- package/dist/workspace/merge-queue/merge-queue.js.map +1 -1
- package/dist/workspace/merge-queue/types.d.ts +16 -2
- package/dist/workspace/merge-queue/types.d.ts.map +1 -1
- package/dist/workspace/merge-queue/types.js +9 -0
- package/dist/workspace/merge-queue/types.js.map +1 -1
- package/dist/workspace/pool/types.d.ts +1 -0
- package/dist/workspace/pool/types.d.ts.map +1 -1
- package/dist/workspace/pool/worktree-pool.d.ts.map +1 -1
- package/dist/workspace/pool/worktree-pool.js +1 -0
- package/dist/workspace/pool/worktree-pool.js.map +1 -1
- package/dist/workspace/recovery/abandon.d.ts +15 -0
- package/dist/workspace/recovery/abandon.d.ts.map +1 -0
- package/dist/workspace/recovery/abandon.js +45 -0
- package/dist/workspace/recovery/abandon.js.map +1 -0
- package/dist/workspace/recovery/auto-resolve.d.ts +27 -0
- package/dist/workspace/recovery/auto-resolve.d.ts.map +1 -0
- package/dist/workspace/recovery/auto-resolve.js +99 -0
- package/dist/workspace/recovery/auto-resolve.js.map +1 -0
- package/dist/workspace/recovery/defer.d.ts +15 -0
- package/dist/workspace/recovery/defer.d.ts.map +1 -0
- package/dist/workspace/recovery/defer.js +16 -0
- package/dist/workspace/recovery/defer.js.map +1 -0
- package/dist/workspace/recovery/escalate.d.ts +16 -0
- package/dist/workspace/recovery/escalate.d.ts.map +1 -0
- package/dist/workspace/recovery/escalate.js +24 -0
- package/dist/workspace/recovery/escalate.js.map +1 -0
- package/dist/workspace/recovery/index.d.ts +32 -0
- package/dist/workspace/recovery/index.d.ts.map +1 -0
- package/dist/workspace/recovery/index.js +45 -0
- package/dist/workspace/recovery/index.js.map +1 -0
- package/dist/workspace/recovery/spawn-resolver.d.ts +45 -0
- package/dist/workspace/recovery/spawn-resolver.d.ts.map +1 -0
- package/dist/workspace/recovery/spawn-resolver.js +111 -0
- package/dist/workspace/recovery/spawn-resolver.js.map +1 -0
- package/dist/workspace/recovery/types.d.ts +63 -0
- package/dist/workspace/recovery/types.d.ts.map +1 -0
- package/dist/workspace/recovery/types.js +12 -0
- package/dist/workspace/recovery/types.js.map +1 -0
- package/dist/workspace/topology/index.d.ts +9 -0
- package/dist/workspace/topology/index.d.ts.map +1 -0
- package/dist/workspace/topology/index.js +8 -0
- package/dist/workspace/topology/index.js.map +1 -0
- package/dist/workspace/topology/no-workspace.d.ts +18 -0
- package/dist/workspace/topology/no-workspace.d.ts.map +1 -0
- package/dist/workspace/topology/no-workspace.js +25 -0
- package/dist/workspace/topology/no-workspace.js.map +1 -0
- package/dist/workspace/topology/types.d.ts +97 -0
- package/dist/workspace/topology/types.d.ts.map +1 -0
- package/dist/workspace/topology/types.js +20 -0
- package/dist/workspace/topology/types.js.map +1 -0
- package/dist/workspace/topology/yaml-driven.d.ts +69 -0
- package/dist/workspace/topology/yaml-driven.d.ts.map +1 -0
- package/dist/workspace/topology/yaml-driven.js +273 -0
- package/dist/workspace/topology/yaml-driven.js.map +1 -0
- package/dist/workspace/types-v3.d.ts +110 -0
- package/dist/workspace/types-v3.d.ts.map +1 -0
- package/dist/workspace/types-v3.js +20 -0
- package/dist/workspace/types-v3.js.map +1 -0
- package/dist/workspace/types.d.ts +145 -17
- package/dist/workspace/types.d.ts.map +1 -1
- package/dist/workspace/workspace-manager.d.ts +92 -13
- package/dist/workspace/workspace-manager.d.ts.map +1 -1
- package/dist/workspace/workspace-manager.js +373 -13
- package/dist/workspace/workspace-manager.js.map +1 -1
- package/dist/workspace/yaml-schema.d.ts +254 -0
- package/dist/workspace/yaml-schema.d.ts.map +1 -0
- package/dist/workspace/yaml-schema.js +170 -0
- package/dist/workspace/yaml-schema.js.map +1 -0
- package/docs/conflict-recovery.md +472 -0
- package/docs/git-cascade-integration-gaps.md +678 -0
- package/docs/workspace-interfaces.md +731 -0
- package/docs/workspace-redesign-plan.md +302 -0
- package/package.json +4 -4
- package/src/__tests__/e2e/auto-sync.e2e.test.ts +257 -0
- package/src/__tests__/e2e/cascade-rebase.e2e.test.ts +254 -0
- package/src/__tests__/e2e/cli-run.e2e.test.ts +167 -0
- package/src/__tests__/e2e/self-driving-v3.e2e.test.ts +197 -0
- package/src/__tests__/e2e/spawn-resolver.e2e.test.ts +200 -0
- package/src/__tests__/e2e/workspace-lifecycle.e2e.test.ts +30 -22
- package/src/__tests__/e2e/workspace-v3.e2e.test.ts +413 -0
- package/src/acp/__tests__/claude-code-replay.test.ts +225 -0
- package/src/acp/__tests__/macro-agent.test.ts +39 -1
- package/src/acp/claude-code-replay.ts +208 -0
- package/src/acp/macro-agent.ts +167 -9
- package/src/acp/types.ts +10 -0
- package/src/agent/__tests__/agent-manager-topology.test.ts +73 -0
- package/src/agent/__tests__/agent-manager-v2.test.ts +66 -0
- package/src/agent/__tests__/task-ref-resolution.test.ts +231 -0
- package/src/agent/agent-manager-v2.ts +293 -48
- package/src/agent/agent-manager.ts +14 -0
- package/src/agent/types.ts +16 -2
- package/src/boot-v2.ts +68 -1
- package/src/cli/index.ts +61 -0
- package/src/cognitive/macro-agent-backend.ts +45 -29
- package/src/integrations/skilltree.ts +1 -0
- package/src/lifecycle/cleanup.ts +52 -3
- package/src/lifecycle/handlers-v2.ts +40 -3
- package/src/lifecycle/types.ts +12 -0
- package/src/map/__tests__/cascade-bridge.test.ts +229 -0
- package/src/map/__tests__/lifecycle-bridge.test.ts +86 -10
- package/src/map/acp-bridge.ts +26 -3
- package/src/map/cascade-bridge.ts +301 -0
- package/src/map/lifecycle-bridge.ts +52 -17
- package/src/map/server.ts +47 -6
- package/src/map/sidecar.ts +31 -1
- package/src/map/types.ts +20 -0
- package/src/mcp/tools/done-v2.ts +9 -0
- package/src/teams/team-manager-v2.ts +37 -0
- package/src/teams/team-runtime-v2.ts +23 -3
- package/src/workspace/__tests__/{dataplane-adapter.test.ts → git-cascade-adapter.test.ts} +209 -14
- package/src/workspace/__tests__/self-driving-yaml.test.ts +114 -0
- package/src/workspace/__tests__/shared-worktree-refcount.test.ts +154 -0
- package/src/workspace/__tests__/standalone-mode.test.ts +118 -0
- package/src/workspace/__tests__/workspace-manager-v3.test.ts +245 -0
- package/src/workspace/__tests__/yaml-schema.test.ts +210 -0
- package/src/workspace/config.ts +11 -11
- package/src/workspace/git-cascade-adapter.ts +1186 -0
- package/src/workspace/index.ts +11 -11
- package/src/workspace/landing/__tests__/strategies.test.ts +142 -0
- package/src/workspace/landing/direct-push.ts +91 -0
- package/src/workspace/landing/index.ts +40 -0
- package/src/workspace/landing/merge-to-parent.ts +228 -0
- package/src/workspace/landing/optimistic-push.ts +36 -0
- package/src/workspace/landing/queue-to-branch.ts +108 -0
- package/src/workspace/merge-queue/merge-queue.ts +10 -0
- package/src/workspace/merge-queue/types.ts +16 -2
- package/src/workspace/pool/__tests__/worktree-pool.integration.test.ts +5 -5
- package/src/workspace/pool/types.ts +1 -0
- package/src/workspace/pool/worktree-pool.ts +1 -0
- package/src/workspace/recovery/__tests__/auto-resolve-integration.test.ts +127 -0
- package/src/workspace/recovery/__tests__/spawn-resolver.test.ts +139 -0
- package/src/workspace/recovery/__tests__/strategies.test.ts +145 -0
- package/src/workspace/recovery/abandon.ts +51 -0
- package/src/workspace/recovery/auto-resolve.ts +119 -0
- package/src/workspace/recovery/defer.ts +23 -0
- package/src/workspace/recovery/escalate.ts +30 -0
- package/src/workspace/recovery/index.ts +58 -0
- package/src/workspace/recovery/spawn-resolver.ts +145 -0
- package/src/workspace/recovery/types.ts +54 -0
- package/src/workspace/topology/__tests__/yaml-driven.test.ts +345 -0
- package/src/workspace/topology/index.ts +18 -0
- package/src/workspace/topology/no-workspace.ts +39 -0
- package/src/workspace/topology/types.ts +116 -0
- package/src/workspace/topology/yaml-driven.ts +316 -0
- package/src/workspace/types-v3.ts +155 -0
- package/src/workspace/types.ts +191 -20
- package/src/workspace/workspace-manager.ts +474 -19
- package/src/workspace/yaml-schema.ts +216 -0
- package/src/workspace/dataplane-adapter.ts +0 -546
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WorkspaceManager Implementation
|
|
3
3
|
*
|
|
4
|
-
* Bridges macro-agent roles to
|
|
4
|
+
* Bridges macro-agent roles to git-cascade streams and worktrees.
|
|
5
5
|
* Provides a higher-level API for workspace management.
|
|
6
6
|
*
|
|
7
7
|
* @module workspace/workspace-manager
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { Stream, WorkerTask, StartTaskResult, AgentWorktree, CleanupWorkerBranchesOptions, CleanupResult } from 'git-cascade';
|
|
12
|
-
import {
|
|
13
|
-
import type {
|
|
12
|
+
import { GitCascadeAdapter } from './git-cascade-adapter.js';
|
|
13
|
+
import type { GitCascadeConfig, WorktreePoolConfig } from './config.js';
|
|
14
14
|
import { DEFAULT_POOL_CONFIG } from './config.js';
|
|
15
15
|
import { WorktreePool } from './pool/worktree-pool.js';
|
|
16
16
|
import type { AllocationStrategy, AcquireOptions } from './pool/types.js';
|
|
@@ -35,7 +35,7 @@ import { execSync } from 'child_process';
|
|
|
35
35
|
/**
|
|
36
36
|
* Configuration options for DefaultWorkspaceManager.
|
|
37
37
|
*/
|
|
38
|
-
export interface WorkspaceManagerConfig extends
|
|
38
|
+
export interface WorkspaceManagerConfig extends GitCascadeConfig {
|
|
39
39
|
/**
|
|
40
40
|
* Base directory for worktrees.
|
|
41
41
|
* Defaults to `<repoPath>/.worktrees`.
|
|
@@ -53,14 +53,14 @@ export interface WorkspaceManagerConfig extends DataplaneConfig {
|
|
|
53
53
|
* DefaultWorkspaceManager implements the WorkspaceManager interface.
|
|
54
54
|
*
|
|
55
55
|
* Responsibilities:
|
|
56
|
-
* - Wraps
|
|
56
|
+
* - Wraps GitCascadeAdapter for stream/worktree operations
|
|
57
57
|
* - Maintains agentId → workspace mappings
|
|
58
58
|
* - Emits events on workspace lifecycle changes
|
|
59
59
|
*
|
|
60
60
|
* @see [[s-7ktd]] WorkspaceManager section
|
|
61
61
|
*/
|
|
62
62
|
export class DefaultWorkspaceManager implements WorkspaceManager {
|
|
63
|
-
private readonly adapter:
|
|
63
|
+
private readonly adapter: GitCascadeAdapter;
|
|
64
64
|
private readonly config: Required<Pick<WorkspaceManagerConfig, 'worktreeBaseDir'>>;
|
|
65
65
|
private readonly poolConfig: WorktreePoolConfig;
|
|
66
66
|
private readonly workspaces: Map<AgentId, Workspace> = new Map();
|
|
@@ -72,10 +72,10 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
|
|
|
72
72
|
/**
|
|
73
73
|
* Create a new DefaultWorkspaceManager.
|
|
74
74
|
*
|
|
75
|
-
* @param adapter -
|
|
75
|
+
* @param adapter - GitCascadeAdapter instance
|
|
76
76
|
* @param config - Configuration options
|
|
77
77
|
*/
|
|
78
|
-
constructor(adapter:
|
|
78
|
+
constructor(adapter: GitCascadeAdapter, config?: Partial<WorkspaceManagerConfig>) {
|
|
79
79
|
this.adapter = adapter;
|
|
80
80
|
this.config = {
|
|
81
81
|
worktreeBaseDir: config?.worktreeBaseDir ?? `${adapter.repoPath}/.worktrees`,
|
|
@@ -86,6 +86,19 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
|
|
|
86
86
|
};
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Access the underlying GitCascadeAdapter. Exposed for bridges that
|
|
91
|
+
* subscribe to the cascade event stream (e.g., the MAP cascade-bridge
|
|
92
|
+
* that forwards events to an OpenHive hub).
|
|
93
|
+
*
|
|
94
|
+
* Most callers should use the WorkspaceManager API surface. Use this only
|
|
95
|
+
* when direct access to the adapter's event stream or primitives is
|
|
96
|
+
* required.
|
|
97
|
+
*/
|
|
98
|
+
getGitCascadeAdapter(): GitCascadeAdapter {
|
|
99
|
+
return this.adapter;
|
|
100
|
+
}
|
|
101
|
+
|
|
89
102
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
90
103
|
// Event System
|
|
91
104
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -370,11 +383,59 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
|
|
|
370
383
|
* @param agentId - ID of the agent whose workspace to deallocate
|
|
371
384
|
*/
|
|
372
385
|
deallocateWorkspace(agentId: AgentId): void {
|
|
386
|
+
// Case 1: agentId is a sharer on someone else's worktree. Decrement
|
|
387
|
+
// the ref-count; only tear down if this was the last sharer AND the
|
|
388
|
+
// owner had previously departed.
|
|
389
|
+
for (const [path, entry] of this.sharedWorktreeRefs) {
|
|
390
|
+
if (entry.sharers.has(agentId)) {
|
|
391
|
+
entry.sharers.delete(agentId);
|
|
392
|
+
this.emit('worktree:released', {
|
|
393
|
+
agentId,
|
|
394
|
+
path,
|
|
395
|
+
kind: 'sharer',
|
|
396
|
+
});
|
|
397
|
+
if (entry.ownerDeparted && entry.sharers.size === 0) {
|
|
398
|
+
// Last sharer exiting after owner departed — finalize teardown
|
|
399
|
+
// under the original owner's id.
|
|
400
|
+
this.adapter.deallocateWorktree(entry.ownerId);
|
|
401
|
+
this.sharedWorktreeRefs.delete(path);
|
|
402
|
+
this.emit('workspace:deallocated', {
|
|
403
|
+
agentId: entry.ownerId,
|
|
404
|
+
path,
|
|
405
|
+
deferredUntilLastSharer: true,
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
373
412
|
const workspace = this.workspaces.get(agentId);
|
|
374
413
|
if (!workspace) {
|
|
375
414
|
return; // Already deallocated
|
|
376
415
|
}
|
|
377
416
|
|
|
417
|
+
// Case 2: agentId is an owner of a shared worktree with active sharers.
|
|
418
|
+
// Defer git-cascade teardown until the last sharer leaves.
|
|
419
|
+
const sharedEntry = this.sharedWorktreeRefs.get(workspace.path);
|
|
420
|
+
if (
|
|
421
|
+
sharedEntry &&
|
|
422
|
+
sharedEntry.ownerId === agentId &&
|
|
423
|
+
sharedEntry.sharers.size > 0
|
|
424
|
+
) {
|
|
425
|
+
sharedEntry.ownerDeparted = true;
|
|
426
|
+
this.workspaces.delete(agentId);
|
|
427
|
+
this.agentToStream.delete(agentId);
|
|
428
|
+
this.emit('worktree:released', {
|
|
429
|
+
agentId,
|
|
430
|
+
path: workspace.path,
|
|
431
|
+
kind: 'owner-departed',
|
|
432
|
+
remainingSharers: sharedEntry.sharers.size,
|
|
433
|
+
});
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Case 3: normal teardown — owner with no sharers (or no sharing involved).
|
|
438
|
+
|
|
378
439
|
// Remove from coordinator's child workspace map if this is a child
|
|
379
440
|
if (workspace.role === 'worker' || workspace.role === 'integrator') {
|
|
380
441
|
const streamId = workspace.streamId;
|
|
@@ -387,14 +448,18 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
|
|
|
387
448
|
}
|
|
388
449
|
}
|
|
389
450
|
|
|
390
|
-
// Deallocate via
|
|
451
|
+
// Deallocate via git-cascade adapter
|
|
391
452
|
this.adapter.deallocateWorktree(agentId);
|
|
392
453
|
|
|
393
454
|
// Clean up mappings
|
|
394
455
|
this.workspaces.delete(agentId);
|
|
395
456
|
this.agentToStream.delete(agentId);
|
|
396
457
|
|
|
397
|
-
//
|
|
458
|
+
// Also clean up any stale entry (owner with zero sharers at deallocation time)
|
|
459
|
+
if (sharedEntry && sharedEntry.ownerId === agentId) {
|
|
460
|
+
this.sharedWorktreeRefs.delete(workspace.path);
|
|
461
|
+
}
|
|
462
|
+
|
|
398
463
|
this.emit('workspace:deallocated', {
|
|
399
464
|
agentId,
|
|
400
465
|
role: workspace.role,
|
|
@@ -511,11 +576,11 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
|
|
|
511
576
|
}
|
|
512
577
|
|
|
513
578
|
/**
|
|
514
|
-
* Get the underlying
|
|
579
|
+
* Get the underlying GitCascadeAdapter.
|
|
515
580
|
*
|
|
516
581
|
* Use with caution - prefer manager methods for operations.
|
|
517
582
|
*/
|
|
518
|
-
get rawAdapter():
|
|
583
|
+
get rawAdapter(): GitCascadeAdapter {
|
|
519
584
|
return this.adapter;
|
|
520
585
|
}
|
|
521
586
|
|
|
@@ -527,7 +592,7 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
|
|
|
527
592
|
* Get the merge queue for coordinating worker merges.
|
|
528
593
|
*
|
|
529
594
|
* The merge queue is lazily initialized on first access and uses
|
|
530
|
-
* the same database as the
|
|
595
|
+
* the same database as the git-cascade adapter.
|
|
531
596
|
*
|
|
532
597
|
* @returns MergeQueue instance
|
|
533
598
|
*/
|
|
@@ -535,7 +600,7 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
|
|
|
535
600
|
if (!this.mergeQueue) {
|
|
536
601
|
this.mergeQueue = new MergeQueue({
|
|
537
602
|
db: this.adapter.db,
|
|
538
|
-
tablePrefix: 'macro_', // Use different prefix from
|
|
603
|
+
tablePrefix: 'macro_', // Use different prefix from git-cascade tables
|
|
539
604
|
initSchema: true,
|
|
540
605
|
});
|
|
541
606
|
}
|
|
@@ -895,7 +960,7 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
|
|
|
895
960
|
}
|
|
896
961
|
}
|
|
897
962
|
|
|
898
|
-
// Release to pool if enabled, otherwise deallocate via
|
|
963
|
+
// Release to pool if enabled, otherwise deallocate via git-cascade
|
|
899
964
|
const pool = this.getPool();
|
|
900
965
|
if (pool) {
|
|
901
966
|
await pool.release(agentId, { clean });
|
|
@@ -963,6 +1028,396 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
|
|
|
963
1028
|
return result;
|
|
964
1029
|
}
|
|
965
1030
|
|
|
1031
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
1032
|
+
// V3 — Stream-first surface
|
|
1033
|
+
//
|
|
1034
|
+
// Additive with the role-shaped methods above. Callers migrate piecewise
|
|
1035
|
+
// during Phases 3-4; Phase 9 removes the legacy methods. See
|
|
1036
|
+
// docs/workspace-interfaces.md §5 and docs/workspace-redesign-plan.md.
|
|
1037
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
1038
|
+
|
|
1039
|
+
private readonly landingStrategies: Map<string, import('./types-v3.js').LandingStrategy> = new Map();
|
|
1040
|
+
|
|
1041
|
+
/**
|
|
1042
|
+
* Ref-counted sharing state keyed by worktree path.
|
|
1043
|
+
*
|
|
1044
|
+
* - `ownerId`: the principal that originally allocated the worktree (the
|
|
1045
|
+
* id that git-cascade's tracker associates with the worktree).
|
|
1046
|
+
* - `sharers`: other agents that allocated via `sharedWithAgent`.
|
|
1047
|
+
* - `ownerDeparted`: set when the owner deallocates but sharers remain —
|
|
1048
|
+
* the actual git-cascade teardown is deferred until `sharers` is empty.
|
|
1049
|
+
*/
|
|
1050
|
+
private readonly sharedWorktreeRefs: Map<
|
|
1051
|
+
string,
|
|
1052
|
+
{ ownerId: import('./types-v3.js').Principal; sharers: Set<AgentId>; ownerDeparted: boolean }
|
|
1053
|
+
> = new Map();
|
|
1054
|
+
|
|
1055
|
+
createStreamV3(spec: import('./types-v3.js').StreamSpec): StreamId {
|
|
1056
|
+
let streamId: StreamId;
|
|
1057
|
+
if (spec.parent) {
|
|
1058
|
+
streamId = this.adapter.forkStream({
|
|
1059
|
+
parentStreamId: spec.parent,
|
|
1060
|
+
name: spec.name,
|
|
1061
|
+
agentId: spec.ownerId,
|
|
1062
|
+
});
|
|
1063
|
+
// git-cascade's ForkStreamOptions doesn't accept metadata; apply via update.
|
|
1064
|
+
if (spec.metadata) {
|
|
1065
|
+
this.adapter.updateStream(streamId, { metadata: spec.metadata });
|
|
1066
|
+
}
|
|
1067
|
+
} else {
|
|
1068
|
+
streamId = this.adapter.createStream({
|
|
1069
|
+
name: spec.name,
|
|
1070
|
+
agentId: spec.ownerId,
|
|
1071
|
+
base: spec.forkFrom ?? 'main',
|
|
1072
|
+
metadata: spec.metadata,
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
this.emit(spec.parent ? 'stream:forked' : 'stream:created', {
|
|
1076
|
+
streamId,
|
|
1077
|
+
ownerId: spec.ownerId,
|
|
1078
|
+
parentStreamId: spec.parent,
|
|
1079
|
+
});
|
|
1080
|
+
return streamId;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
forkStream(opts: {
|
|
1084
|
+
parentStreamId: StreamId;
|
|
1085
|
+
name: string;
|
|
1086
|
+
ownerId: import('./types-v3.js').Principal;
|
|
1087
|
+
metadata?: Record<string, unknown>;
|
|
1088
|
+
}): StreamId {
|
|
1089
|
+
const streamId = this.adapter.forkStream({
|
|
1090
|
+
parentStreamId: opts.parentStreamId,
|
|
1091
|
+
name: opts.name,
|
|
1092
|
+
agentId: opts.ownerId,
|
|
1093
|
+
});
|
|
1094
|
+
if (opts.metadata) {
|
|
1095
|
+
this.adapter.updateStream(streamId, { metadata: opts.metadata });
|
|
1096
|
+
}
|
|
1097
|
+
this.emit('stream:forked', {
|
|
1098
|
+
streamId,
|
|
1099
|
+
parentStreamId: opts.parentStreamId,
|
|
1100
|
+
ownerId: opts.ownerId,
|
|
1101
|
+
});
|
|
1102
|
+
return streamId;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
mergeStream(opts: {
|
|
1106
|
+
sourceStreamId: StreamId;
|
|
1107
|
+
targetStreamId: StreamId;
|
|
1108
|
+
agentId: import('./types-v3.js').Principal;
|
|
1109
|
+
worktree: string;
|
|
1110
|
+
}): import('./types-v3.js').MergeResult {
|
|
1111
|
+
// git-cascade's MergeStreamOptions uses `sourceStream`/`targetStream`.
|
|
1112
|
+
// We adapt to v3's `sourceStreamId`/`targetStreamId` at the boundary.
|
|
1113
|
+
const result = this.adapter.mergeStream({
|
|
1114
|
+
sourceStream: opts.sourceStreamId,
|
|
1115
|
+
targetStream: opts.targetStreamId,
|
|
1116
|
+
agentId: opts.agentId,
|
|
1117
|
+
worktree: opts.worktree,
|
|
1118
|
+
});
|
|
1119
|
+
if (result.success) {
|
|
1120
|
+
this.emit('stream:merged', {
|
|
1121
|
+
sourceStreamId: opts.sourceStreamId,
|
|
1122
|
+
targetStreamId: opts.targetStreamId,
|
|
1123
|
+
mergeCommit: result.newHead,
|
|
1124
|
+
});
|
|
1125
|
+
} else {
|
|
1126
|
+
this.emit('stream:conflicted', {
|
|
1127
|
+
streamId: opts.sourceStreamId,
|
|
1128
|
+
conflicts: result.conflicts,
|
|
1129
|
+
error: result.error,
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
return result;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
syncWithParent(opts: {
|
|
1136
|
+
streamId: StreamId;
|
|
1137
|
+
agentId: import('./types-v3.js').Principal;
|
|
1138
|
+
worktree: string;
|
|
1139
|
+
onConflict?: import('./types-v3.js').ConflictStrategy;
|
|
1140
|
+
}): import('./types-v3.js').RebaseResult {
|
|
1141
|
+
return this.adapter.syncWithParent(
|
|
1142
|
+
opts.streamId,
|
|
1143
|
+
opts.agentId,
|
|
1144
|
+
opts.worktree,
|
|
1145
|
+
opts.onConflict
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
// abandonStream, pauseStream, resumeStream are inherited-by-name from the
|
|
1150
|
+
// adapter calls; expose thin wrappers that emit workspace-level events.
|
|
1151
|
+
|
|
1152
|
+
abandonStream(streamId: StreamId, opts?: { cascade?: boolean; reason?: string }): void {
|
|
1153
|
+
this.adapter.abandonStream(streamId, opts);
|
|
1154
|
+
this.emit('stream:abandoned', { streamId, ...opts });
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
pauseStream(streamId: StreamId, reason?: string): void {
|
|
1158
|
+
this.adapter.pauseStream(streamId, reason);
|
|
1159
|
+
this.emit('stream:paused', { streamId, reason });
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
resumeStream(streamId: StreamId): void {
|
|
1163
|
+
this.adapter.resumeStream(streamId);
|
|
1164
|
+
this.emit('stream:resumed', { streamId });
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
listStreams(filter?: {
|
|
1168
|
+
ownerId?: import('./types-v3.js').Principal;
|
|
1169
|
+
status?: import('./types-v3.js').Stream['status'];
|
|
1170
|
+
}): import('./types-v3.js').Stream[] {
|
|
1171
|
+
return this.adapter.listStreams({
|
|
1172
|
+
agentId: filter?.ownerId,
|
|
1173
|
+
status: filter?.status,
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
commitChanges(opts: {
|
|
1178
|
+
agentId: import('./types-v3.js').Principal;
|
|
1179
|
+
streamId: StreamId;
|
|
1180
|
+
worktree: string;
|
|
1181
|
+
message: string;
|
|
1182
|
+
}): { commit: string; changeId: import('./types-v3.js').ChangeId } {
|
|
1183
|
+
const result = this.adapter.commitChanges(opts);
|
|
1184
|
+
this.emit('stream:committed', {
|
|
1185
|
+
streamId: opts.streamId,
|
|
1186
|
+
commit: result.commit,
|
|
1187
|
+
changeId: result.changeId,
|
|
1188
|
+
agentId: opts.agentId,
|
|
1189
|
+
});
|
|
1190
|
+
return result;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
markChangesMerged(changeIds: import('./types-v3.js').ChangeId[]): void {
|
|
1194
|
+
this.adapter.markChangesMerged(changeIds);
|
|
1195
|
+
for (const id of changeIds) {
|
|
1196
|
+
this.emit('change:merged', { changeId: id });
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
getChange(changeId: import('./types-v3.js').ChangeId): import('./types-v3.js').Change | null {
|
|
1201
|
+
return this.adapter.getChange(changeId);
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
getChangeByCommit(commit: string): import('./types-v3.js').Change | null {
|
|
1205
|
+
return this.adapter.getChangeByCommit(commit);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
allocateWorktree(
|
|
1209
|
+
opts: import('./types-v3.js').AllocateWorktreeOpts
|
|
1210
|
+
): import('./types-v3.js').Worktree {
|
|
1211
|
+
// Ref-counted sharing: if sharedWithAgent is set, reuse that agent's worktree
|
|
1212
|
+
if (opts.sharedWithAgent) {
|
|
1213
|
+
const owner = this.adapter.getWorktree(opts.sharedWithAgent);
|
|
1214
|
+
if (!owner) {
|
|
1215
|
+
throw new Error(
|
|
1216
|
+
`Cannot share worktree: agent ${opts.sharedWithAgent} has no allocated worktree`
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
let entry = this.sharedWorktreeRefs.get(owner.path);
|
|
1220
|
+
if (!entry) {
|
|
1221
|
+
entry = {
|
|
1222
|
+
ownerId: opts.sharedWithAgent,
|
|
1223
|
+
sharers: new Set<AgentId>(),
|
|
1224
|
+
ownerDeparted: false,
|
|
1225
|
+
};
|
|
1226
|
+
this.sharedWorktreeRefs.set(owner.path, entry);
|
|
1227
|
+
}
|
|
1228
|
+
if (entry.ownerDeparted && entry.sharers.size === 0) {
|
|
1229
|
+
// Edge case: owner already left and all sharers left, but someone
|
|
1230
|
+
// is still trying to share. Reject — the teardown has been staged
|
|
1231
|
+
// but this would resurrect a dead reference.
|
|
1232
|
+
throw new Error(
|
|
1233
|
+
`Cannot share worktree at ${owner.path}: owner has departed and no active sharers`
|
|
1234
|
+
);
|
|
1235
|
+
}
|
|
1236
|
+
entry.sharers.add(opts.agentId);
|
|
1237
|
+
this.emit('worktree:shared', {
|
|
1238
|
+
path: owner.path,
|
|
1239
|
+
ownerAgentId: opts.sharedWithAgent,
|
|
1240
|
+
sharingAgentId: opts.agentId,
|
|
1241
|
+
});
|
|
1242
|
+
return owner;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
// Fresh worktree
|
|
1246
|
+
const baseDir = opts.baseDir ?? this.config.worktreeBaseDir;
|
|
1247
|
+
const sanitizedId = opts.agentId.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
1248
|
+
const path = `${baseDir}/${sanitizedId}`;
|
|
1249
|
+
|
|
1250
|
+
const worktreeArgs: import('git-cascade').CreateWorktreeOptions = {
|
|
1251
|
+
agentId: opts.agentId,
|
|
1252
|
+
path,
|
|
1253
|
+
};
|
|
1254
|
+
if (opts.streamId) {
|
|
1255
|
+
worktreeArgs.branch = opts.branch ?? this.adapter.getStreamBranchName(opts.streamId);
|
|
1256
|
+
} else if (opts.branch) {
|
|
1257
|
+
worktreeArgs.branch = opts.branch;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
const worktree = this.adapter.createWorktree(worktreeArgs);
|
|
1261
|
+
|
|
1262
|
+
if (opts.streamId) {
|
|
1263
|
+
this.agentToStream.set(opts.agentId, opts.streamId);
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
// Track in workspaces map so legacy deallocateWorkspace can find this
|
|
1267
|
+
// V3-allocated worktree. Use role='v3' to bypass the legacy
|
|
1268
|
+
// worker/integrator coordinator-map cleanup path.
|
|
1269
|
+
this.workspaces.set(opts.agentId as AgentId, {
|
|
1270
|
+
agentId: opts.agentId as AgentId,
|
|
1271
|
+
path: worktree.path,
|
|
1272
|
+
branch: worktree.currentStream
|
|
1273
|
+
? `stream/${worktree.currentStream}`
|
|
1274
|
+
: (opts.branch ?? 'unknown'),
|
|
1275
|
+
streamId: opts.streamId ?? '',
|
|
1276
|
+
role: 'v3',
|
|
1277
|
+
createdAt: worktree.createdAt,
|
|
1278
|
+
});
|
|
1279
|
+
|
|
1280
|
+
this.emit('worktree:allocated', {
|
|
1281
|
+
agentId: opts.agentId,
|
|
1282
|
+
path: worktree.path,
|
|
1283
|
+
streamId: opts.streamId,
|
|
1284
|
+
});
|
|
1285
|
+
return worktree;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
getWorktreeForAgent(
|
|
1289
|
+
agentId: import('./types-v3.js').Principal
|
|
1290
|
+
): import('./types-v3.js').Worktree | null {
|
|
1291
|
+
return this.adapter.getWorktree(agentId);
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
registerLandingStrategy(strategy: import('./types-v3.js').LandingStrategy): void {
|
|
1295
|
+
this.landingStrategies.set(strategy.name, strategy);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
reconcileV3(): import('./types-v3.js').MacroReconcileResult {
|
|
1299
|
+
const result: import('./types-v3.js').MacroReconcileResult = {
|
|
1300
|
+
streamsChecked: 0,
|
|
1301
|
+
streamsFixed: 0,
|
|
1302
|
+
worktreesOrphaned: 0,
|
|
1303
|
+
worktreesCleaned: 0,
|
|
1304
|
+
poolEntriesPurged: 0,
|
|
1305
|
+
errors: [],
|
|
1306
|
+
};
|
|
1307
|
+
|
|
1308
|
+
// Delegate stream↔git sync to git-cascade
|
|
1309
|
+
try {
|
|
1310
|
+
const gcResult = this.adapter.reconcile();
|
|
1311
|
+
result.streamsChecked = (gcResult.updated?.length ?? 0) +
|
|
1312
|
+
(gcResult.branchesCreated?.length ?? 0) +
|
|
1313
|
+
(gcResult.failed?.length ?? 0);
|
|
1314
|
+
result.streamsFixed = (gcResult.updated?.length ?? 0) +
|
|
1315
|
+
(gcResult.branchesCreated?.length ?? 0);
|
|
1316
|
+
for (const f of gcResult.failed ?? []) {
|
|
1317
|
+
result.errors.push({
|
|
1318
|
+
context: `stream ${f.streamId}`,
|
|
1319
|
+
message: f.error,
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
} catch (err) {
|
|
1323
|
+
result.errors.push({
|
|
1324
|
+
context: 'git-cascade reconcile',
|
|
1325
|
+
message: err instanceof Error ? err.message : String(err),
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// Worktree pool and orphan cleanup deferred to Phase 3 (when TopologyPolicy
|
|
1330
|
+
// owns worktree lifecycle). For now, just count what git-cascade knows about.
|
|
1331
|
+
try {
|
|
1332
|
+
const worktrees = this.adapter.listWorktrees();
|
|
1333
|
+
// An "orphan" is a worktree without a corresponding record in our tracking
|
|
1334
|
+
// map. Count only; don't delete here — that's reserved for Phase 3.
|
|
1335
|
+
for (const wt of worktrees) {
|
|
1336
|
+
if (!this.workspaces.has(wt.agentId) && !this.agentToStream.has(wt.agentId)) {
|
|
1337
|
+
result.worktreesOrphaned++;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
} catch (err) {
|
|
1341
|
+
result.errors.push({
|
|
1342
|
+
context: 'worktree orphan scan',
|
|
1343
|
+
message: err instanceof Error ? err.message : String(err),
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
return result;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
resolveConflict(opts: {
|
|
1351
|
+
conflictId: string;
|
|
1352
|
+
resolvedBy: import('./types-v3.js').Principal;
|
|
1353
|
+
resolutionCommit?: string;
|
|
1354
|
+
/**
|
|
1355
|
+
* How the conflict was resolved. Defaults to 'agent' for the legacy
|
|
1356
|
+
* call shape; recovery strategies should pass an explicit method so
|
|
1357
|
+
* the OpenHive hub records the right resolution.
|
|
1358
|
+
*/
|
|
1359
|
+
method?: import('git-cascade').ConflictResolution['method'] | 'auto-resolve' | 'spawn-resolver' | 'abandoned';
|
|
1360
|
+
/** Human-readable resolution summary (e.g., 'merged with -X ours'). */
|
|
1361
|
+
summary?: string;
|
|
1362
|
+
}): void {
|
|
1363
|
+
const conflict = this.adapter.getConflict(opts.conflictId);
|
|
1364
|
+
const method = opts.method ?? 'agent';
|
|
1365
|
+
|
|
1366
|
+
// Drive git-cascade's resolveConflict so the underlying conflict record
|
|
1367
|
+
// moves to status='resolved' AND the tracker emits stream.conflict_resolved.
|
|
1368
|
+
// Hub observers (cascade-bridge → OpenHive) update cascade_conflicts.status
|
|
1369
|
+
// accordingly. Falls back gracefully if cascade is older than 0.0.6.
|
|
1370
|
+
const trackerHasResolve = typeof (
|
|
1371
|
+
this.adapter as { resolveConflict?: unknown }
|
|
1372
|
+
).resolveConflict === 'function';
|
|
1373
|
+
if (trackerHasResolve) {
|
|
1374
|
+
try {
|
|
1375
|
+
(
|
|
1376
|
+
this.adapter as unknown as {
|
|
1377
|
+
resolveConflict(args: {
|
|
1378
|
+
conflictId: string;
|
|
1379
|
+
resolution: import('git-cascade').ConflictResolution & { summary?: string };
|
|
1380
|
+
metadata?: Record<string, unknown>;
|
|
1381
|
+
}): void;
|
|
1382
|
+
}
|
|
1383
|
+
).resolveConflict({
|
|
1384
|
+
conflictId: opts.conflictId,
|
|
1385
|
+
resolution: {
|
|
1386
|
+
method:
|
|
1387
|
+
method === 'auto-resolve' ||
|
|
1388
|
+
method === 'spawn-resolver' ||
|
|
1389
|
+
method === 'abandoned'
|
|
1390
|
+
? 'agent'
|
|
1391
|
+
: method,
|
|
1392
|
+
resolvedBy: opts.resolvedBy,
|
|
1393
|
+
details: opts.summary,
|
|
1394
|
+
},
|
|
1395
|
+
metadata: {
|
|
1396
|
+
resolution_method_actual: method,
|
|
1397
|
+
resolution_commit: opts.resolutionCommit,
|
|
1398
|
+
},
|
|
1399
|
+
});
|
|
1400
|
+
} catch {
|
|
1401
|
+
// Best-effort; legacy resume path below remains.
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
if (conflict?.streamId) {
|
|
1406
|
+
try {
|
|
1407
|
+
this.adapter.resumeStream(conflict.streamId);
|
|
1408
|
+
} catch {
|
|
1409
|
+
// Stream may not be paused; safe to ignore.
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
this.emit('conflict:resolved', {
|
|
1414
|
+
conflictId: opts.conflictId,
|
|
1415
|
+
resolvedBy: opts.resolvedBy,
|
|
1416
|
+
resolutionCommit: opts.resolutionCommit,
|
|
1417
|
+
streamId: conflict?.streamId,
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
|
|
966
1421
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
967
1422
|
// Lifecycle
|
|
968
1423
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -1044,19 +1499,19 @@ export class DefaultWorkspaceManager implements WorkspaceManager {
|
|
|
1044
1499
|
export function createWorkspaceManager(
|
|
1045
1500
|
config: WorkspaceManagerConfig
|
|
1046
1501
|
): DefaultWorkspaceManager {
|
|
1047
|
-
const adapter = new
|
|
1502
|
+
const adapter = new GitCascadeAdapter(config);
|
|
1048
1503
|
return new DefaultWorkspaceManager(adapter, config);
|
|
1049
1504
|
}
|
|
1050
1505
|
|
|
1051
1506
|
/**
|
|
1052
|
-
* Create a WorkspaceManager with an existing
|
|
1507
|
+
* Create a WorkspaceManager with an existing GitCascadeAdapter.
|
|
1053
1508
|
*
|
|
1054
|
-
* @param adapter -
|
|
1509
|
+
* @param adapter - GitCascadeAdapter instance
|
|
1055
1510
|
* @param config - Configuration options
|
|
1056
1511
|
* @returns WorkspaceManager instance
|
|
1057
1512
|
*/
|
|
1058
1513
|
export function createWorkspaceManagerWithAdapter(
|
|
1059
|
-
adapter:
|
|
1514
|
+
adapter: GitCascadeAdapter,
|
|
1060
1515
|
config?: Partial<WorkspaceManagerConfig>
|
|
1061
1516
|
): DefaultWorkspaceManager {
|
|
1062
1517
|
return new DefaultWorkspaceManager(adapter, config);
|