macro-agent 0.1.8 → 0.1.11
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 +263 -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 +192 -7
- 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/adapters/tasks-adapter.d.ts.map +1 -1
- package/dist/adapters/tasks-adapter.js +3 -0
- package/dist/adapters/tasks-adapter.js.map +1 -1
- package/dist/adapters/types.d.ts +1 -0
- package/dist/adapters/types.d.ts.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 +308 -54
- 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/agent-store.d.ts +10 -0
- package/dist/agent/agent-store.d.ts.map +1 -1
- package/dist/agent/agent-store.js +22 -0
- package/dist/agent/agent-store.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 +129 -1
- package/dist/boot-v2.d.ts.map +1 -1
- package/dist/boot-v2.js +359 -8
- package/dist/boot-v2.js.map +1 -1
- package/dist/cli/acp.js +4 -0
- package/dist/cli/acp.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/cascade.d.ts +25 -2
- package/dist/lifecycle/cascade.d.ts.map +1 -1
- package/dist/lifecycle/cascade.js +70 -2
- package/dist/lifecycle/cascade.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-action-handler.d.ts +24 -0
- package/dist/map/cascade-action-handler.d.ts.map +1 -0
- package/dist/map/cascade-action-handler.js +170 -0
- package/dist/map/cascade-action-handler.js.map +1 -0
- 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 +294 -0
- package/dist/map/cascade-bridge.js.map +1 -0
- package/dist/map/coordination-handler.d.ts.map +1 -1
- package/dist/map/coordination-handler.js +12 -1
- package/dist/map/coordination-handler.js.map +1 -1
- 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 +219 -7
- package/dist/map/server.js.map +1 -1
- package/dist/map/sidecar.d.ts.map +1 -1
- package/dist/map/sidecar.js +49 -2
- package/dist/map/sidecar.js.map +1 -1
- package/dist/map/types.d.ts +22 -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 +934 -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 +186 -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 +118 -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 +117 -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 +162 -17
- package/dist/workspace/types.d.ts.map +1 -1
- package/dist/workspace/workspace-manager.d.ts +101 -13
- package/dist/workspace/workspace-manager.d.ts.map +1 -1
- package/dist/workspace/workspace-manager.js +416 -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/design/task-dispatcher.md +880 -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 +6 -5
- package/src/__tests__/boot-v2.test.ts +435 -0
- package/src/__tests__/e2e/acp-over-map.e2e.test.ts +92 -0
- package/src/__tests__/e2e/auto-sync.e2e.test.ts +257 -0
- package/src/__tests__/e2e/bootstrap.e2e.test.ts +319 -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/dispatch-coordination.e2e.test.ts +495 -0
- package/src/__tests__/e2e/dispatch-live.e2e.test.ts +564 -0
- package/src/__tests__/e2e/dispatch-opentasks.e2e.test.ts +496 -0
- package/src/__tests__/e2e/dispatch-phase2-live.e2e.test.ts +456 -0
- package/src/__tests__/e2e/dispatch-phase2.e2e.test.ts +386 -0
- package/src/__tests__/e2e/dispatch.e2e.test.ts +376 -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 +203 -10
- package/src/acp/types.ts +10 -0
- package/src/adapters/__tests__/tasks-adapter.test.ts +1 -0
- package/src/adapters/tasks-adapter.ts +3 -0
- package/src/adapters/types.ts +1 -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__/agent-store.test.ts +52 -0
- package/src/agent/__tests__/task-ref-resolution.test.ts +231 -0
- package/src/agent/agent-manager-v2.ts +372 -59
- package/src/agent/agent-manager.ts +14 -0
- package/src/agent/agent-store.ts +24 -0
- package/src/agent/types.ts +16 -2
- package/src/boot-v2.ts +589 -35
- package/src/cli/acp.ts +4 -0
- 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/__tests__/cascade-consolidation.test.ts +240 -0
- package/src/lifecycle/cascade.ts +77 -2
- 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__/emit-event.test.ts +71 -0
- package/src/map/__tests__/lifecycle-bridge.test.ts +86 -10
- package/src/map/acp-bridge.ts +26 -3
- package/src/map/cascade-action-handler.ts +205 -0
- package/src/map/cascade-bridge.ts +339 -0
- package/src/map/coordination-handler.ts +13 -1
- package/src/map/lifecycle-bridge.ts +52 -17
- package/src/map/server.ts +225 -7
- package/src/map/sidecar.ts +48 -1
- package/src/map/types.ts +23 -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__/land-dispatch.test.ts +214 -0
- 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 +1213 -0
- package/src/workspace/index.ts +11 -11
- package/src/workspace/landing/__tests__/strategies.test.ts +184 -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 +229 -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 +152 -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 +162 -0
- package/src/workspace/types.ts +211 -20
- package/src/workspace/workspace-manager.ts +533 -19
- package/src/workspace/yaml-schema.ts +216 -0
- package/dist/workspace/dataplane-adapter.d.ts +0 -260
- package/dist/workspace/dataplane-adapter.d.ts.map +0 -1
- package/dist/workspace/dataplane-adapter.js +0 -416
- package/dist/workspace/dataplane-adapter.js.map +0 -1
- package/src/workspace/dataplane-adapter.ts +0 -546
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YamlDrivenTopology — primary TopologyPolicy implementation.
|
|
3
|
+
*
|
|
4
|
+
* Compiles `TeamWorkspaceConfig` into per-spawn workspace decisions. Covers
|
|
5
|
+
* all 6 workflows in `docs/git-cascade-integration-gaps.md` §5 through YAML
|
|
6
|
+
* alone.
|
|
7
|
+
*
|
|
8
|
+
* @module workspace/topology/yaml-driven
|
|
9
|
+
* @see docs/workspace-redesign-plan.md Phase 3
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { AgentId, StreamId, Principal } from '../types-v3.js';
|
|
13
|
+
import type {
|
|
14
|
+
TeamWorkspaceConfig,
|
|
15
|
+
RoleWorkspaceConfig,
|
|
16
|
+
StreamLineage,
|
|
17
|
+
} from '../yaml-schema.js';
|
|
18
|
+
import type {
|
|
19
|
+
TopologyPolicy,
|
|
20
|
+
TeamStartContext,
|
|
21
|
+
TeamStartPlan,
|
|
22
|
+
SpawnContext,
|
|
23
|
+
WorkspaceDecision,
|
|
24
|
+
AgentCompleteContext,
|
|
25
|
+
TeamStopContext,
|
|
26
|
+
} from './types.js';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Topology policy driven entirely by `macro_agent.workspace` YAML.
|
|
30
|
+
*/
|
|
31
|
+
export class YamlDrivenTopology implements TopologyPolicy {
|
|
32
|
+
readonly name = 'yaml-driven';
|
|
33
|
+
|
|
34
|
+
private teamStreamId?: StreamId;
|
|
35
|
+
private readonly agentStreams: Map<AgentId, StreamId> = new Map();
|
|
36
|
+
/** Tracks which role each live agent is, so sync-with-parent can dispatch. */
|
|
37
|
+
private readonly agentRoles: Map<AgentId, string> = new Map();
|
|
38
|
+
/** Unsubscribe from the workspace event stream; set on onTeamStart, cleared on onTeamStop. */
|
|
39
|
+
private eventUnsubscribe?: () => void;
|
|
40
|
+
/** Debounce map: stream id → last-sync timestamp (ms) for coalescing. */
|
|
41
|
+
private readonly lastSyncAt: Map<StreamId, number> = new Map();
|
|
42
|
+
/** Minimum interval between auto-syncs per parent stream (ms). */
|
|
43
|
+
private static readonly SYNC_COALESCE_MS = 2_000;
|
|
44
|
+
|
|
45
|
+
constructor(private readonly config: TeamWorkspaceConfig) {}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Look up per-role config. Returns null for roles not declared in YAML.
|
|
49
|
+
*/
|
|
50
|
+
getRoleConfig(role: string): RoleWorkspaceConfig | null {
|
|
51
|
+
return this.config.roles[role] ?? null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async onTeamStart(ctx: TeamStartContext): Promise<TeamStartPlan> {
|
|
55
|
+
const needsTeamRoot = this.teamNeedsRootStream();
|
|
56
|
+
|
|
57
|
+
if (needsTeamRoot) {
|
|
58
|
+
const forkFrom = this.config.default_stream?.fork_from ?? 'main';
|
|
59
|
+
const nameTemplate = this.config.default_stream?.name_template ?? '{team}';
|
|
60
|
+
const streamName = nameTemplate.replace('{team}', ctx.teamName);
|
|
61
|
+
|
|
62
|
+
this.teamStreamId = ctx.workspaceManager.createStreamV3({
|
|
63
|
+
name: streamName,
|
|
64
|
+
ownerId: `team:${ctx.teamName}` as const,
|
|
65
|
+
forkFrom,
|
|
66
|
+
metadata: { kind: 'team_root', teamInstanceId: ctx.teamInstanceId },
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Wire auto-sync if any role declared on_parent_advanced: sync_with_parent.
|
|
71
|
+
if (this.hasAutoSyncRoles()) {
|
|
72
|
+
this.eventUnsubscribe = ctx.workspaceManager.onEvent((event) => {
|
|
73
|
+
if (event.type !== 'stream:committed') return;
|
|
74
|
+
const streamId = event.data.streamId as StreamId | undefined;
|
|
75
|
+
if (!streamId) return;
|
|
76
|
+
// Fire-and-forget: schedule sync for affected agents on children of
|
|
77
|
+
// this stream, coalesced.
|
|
78
|
+
void this.dispatchSync(ctx, streamId);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return this.teamStreamId
|
|
83
|
+
? { teamStreamId: this.teamStreamId }
|
|
84
|
+
: {};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async onAgentSpawn(ctx: SpawnContext): Promise<WorkspaceDecision> {
|
|
88
|
+
const roleConfig = this.getRoleConfig(ctx.role);
|
|
89
|
+
if (!roleConfig) {
|
|
90
|
+
// Role not declared in macro_agent.workspace → conservative default:
|
|
91
|
+
// inherit parent's cwd (no isolation, no stream).
|
|
92
|
+
return { kind: 'share-parent-cwd' };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
switch (roleConfig.workspace) {
|
|
96
|
+
case 'none':
|
|
97
|
+
return { kind: 'none' };
|
|
98
|
+
|
|
99
|
+
case 'share_parent_cwd':
|
|
100
|
+
return { kind: 'share-parent-cwd' };
|
|
101
|
+
|
|
102
|
+
case 'share_with_agent': {
|
|
103
|
+
if (!roleConfig.share_with) {
|
|
104
|
+
// Schema guarantees this, but double-check
|
|
105
|
+
return { kind: 'share-parent-cwd' };
|
|
106
|
+
}
|
|
107
|
+
const partnerId = ctx.getAgentByRole?.(roleConfig.share_with);
|
|
108
|
+
if (!partnerId) {
|
|
109
|
+
// Partner role not yet spawned; fall back to share-parent-cwd.
|
|
110
|
+
return { kind: 'share-parent-cwd' };
|
|
111
|
+
}
|
|
112
|
+
return { kind: 'share-with-agent', agentId: partnerId };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
case 'attach_to_team_root': {
|
|
116
|
+
if (!this.teamStreamId) {
|
|
117
|
+
// Should have been created in onTeamStart; defensive fallback
|
|
118
|
+
return { kind: 'share-parent-cwd' };
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
kind: 'attach-to-stream',
|
|
122
|
+
streamId: this.teamStreamId,
|
|
123
|
+
allocateWorktree: roleConfig.allocation !== 'inherit_parent_cwd',
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
case 'new_stream': {
|
|
128
|
+
const parent = this.resolveParentStream(roleConfig, ctx);
|
|
129
|
+
const forkFrom =
|
|
130
|
+
roleConfig.stream_lineage === 'independent'
|
|
131
|
+
? this.config.default_stream?.fork_from ?? 'main'
|
|
132
|
+
: undefined;
|
|
133
|
+
const streamName = this.buildStreamName(ctx.role, ctx.agentId);
|
|
134
|
+
|
|
135
|
+
const spec = {
|
|
136
|
+
name: streamName,
|
|
137
|
+
ownerId: ctx.agentId,
|
|
138
|
+
parent,
|
|
139
|
+
forkFrom,
|
|
140
|
+
metadata: { role: ctx.role },
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
kind: 'new-stream',
|
|
145
|
+
streamSpec: spec,
|
|
146
|
+
allocateWorktree: roleConfig.allocation !== 'inherit_parent_cwd',
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async onAgentComplete(ctx: AgentCompleteContext): Promise<void> {
|
|
153
|
+
// Deallocate the agent's worktree (if any). Landing was already handled
|
|
154
|
+
// by the LandingStrategy invoked from done(); this is pure cleanup.
|
|
155
|
+
try {
|
|
156
|
+
ctx.workspaceManager.deallocateWorkspace(ctx.agentId);
|
|
157
|
+
} catch {
|
|
158
|
+
// Non-fatal — agent may not have had a worktree
|
|
159
|
+
}
|
|
160
|
+
this.agentStreams.delete(ctx.agentId);
|
|
161
|
+
this.agentRoles.delete(ctx.agentId);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async onTeamStop(ctx: TeamStopContext): Promise<void> {
|
|
165
|
+
// Stop auto-sync event subscription
|
|
166
|
+
if (this.eventUnsubscribe) {
|
|
167
|
+
this.eventUnsubscribe();
|
|
168
|
+
this.eventUnsubscribe = undefined;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!this.teamStreamId) return;
|
|
172
|
+
|
|
173
|
+
const action = this.config.on_team_complete;
|
|
174
|
+
switch (action) {
|
|
175
|
+
case 'abandon':
|
|
176
|
+
ctx.workspaceManager.abandonStream(this.teamStreamId, {
|
|
177
|
+
cascade: true,
|
|
178
|
+
reason: 'team stopped',
|
|
179
|
+
});
|
|
180
|
+
break;
|
|
181
|
+
case 'merge_to_main':
|
|
182
|
+
// Requires a landing strategy configured for the team stream.
|
|
183
|
+
// Deferred to Phase 5 (LandingStrategy integration); log a warning.
|
|
184
|
+
// Leaving stream active for now.
|
|
185
|
+
break;
|
|
186
|
+
case 'keep':
|
|
187
|
+
default:
|
|
188
|
+
// Leave the stream active for human review / PR.
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Track agent→stream mapping after a successful spawn. Called externally
|
|
195
|
+
* after the WorkspaceDecision is executed; lets the policy record state.
|
|
196
|
+
*/
|
|
197
|
+
recordAgentStream(agentId: AgentId, streamId: StreamId, role?: string): void {
|
|
198
|
+
this.agentStreams.set(agentId, streamId);
|
|
199
|
+
if (role) this.agentRoles.set(agentId, role);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get the stream attached to an agent (from our local tracking).
|
|
204
|
+
*/
|
|
205
|
+
getAgentStream(agentId: AgentId): StreamId | null {
|
|
206
|
+
return this.agentStreams.get(agentId) ?? null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ── Helpers ─────────────────────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* A team needs a root stream if any role uses team-root-relative lineage or
|
|
213
|
+
* attaches to the team root.
|
|
214
|
+
*/
|
|
215
|
+
private teamNeedsRootStream(): boolean {
|
|
216
|
+
for (const roleConfig of Object.values(this.config.roles)) {
|
|
217
|
+
if (roleConfig.workspace === 'attach_to_team_root') return true;
|
|
218
|
+
if (
|
|
219
|
+
roleConfig.workspace === 'new_stream' &&
|
|
220
|
+
(roleConfig.stream_lineage === 'from_team_root' ||
|
|
221
|
+
roleConfig.stream_lineage === 'fork_from_team_root')
|
|
222
|
+
) {
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private resolveParentStream(
|
|
230
|
+
roleConfig: RoleWorkspaceConfig,
|
|
231
|
+
ctx: SpawnContext
|
|
232
|
+
): StreamId | undefined {
|
|
233
|
+
const lineage = roleConfig.stream_lineage;
|
|
234
|
+
switch (lineage) {
|
|
235
|
+
case 'fork_from_team_root':
|
|
236
|
+
case 'from_team_root':
|
|
237
|
+
return this.teamStreamId;
|
|
238
|
+
case 'fork_from_parent':
|
|
239
|
+
return ctx.parentStreamId ?? this.teamStreamId;
|
|
240
|
+
case 'independent':
|
|
241
|
+
return undefined;
|
|
242
|
+
case 'track_existing_branch':
|
|
243
|
+
return undefined; // Caller uses track_branch directly
|
|
244
|
+
default:
|
|
245
|
+
return undefined;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private buildStreamName(role: string, agentId: AgentId): string {
|
|
250
|
+
const shortId = agentId.slice(-8);
|
|
251
|
+
return `${role}-${shortId}`;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* True if any role declared `on_parent_advanced: sync_with_parent`.
|
|
256
|
+
* Used to decide whether to subscribe to stream:committed events at all.
|
|
257
|
+
*/
|
|
258
|
+
private hasAutoSyncRoles(): boolean {
|
|
259
|
+
for (const roleConfig of Object.values(this.config.roles)) {
|
|
260
|
+
if (roleConfig.on_parent_advanced === 'sync_with_parent') return true;
|
|
261
|
+
}
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Dispatch syncWithParent for live agents whose role has
|
|
267
|
+
* `on_parent_advanced: sync_with_parent` and whose active stream's parent
|
|
268
|
+
* is `parentStreamId`.
|
|
269
|
+
*
|
|
270
|
+
* Coalesces: skips if a sync fired for this parent within SYNC_COALESCE_MS.
|
|
271
|
+
*/
|
|
272
|
+
private async dispatchSync(
|
|
273
|
+
ctx: TeamStartContext,
|
|
274
|
+
parentStreamId: StreamId
|
|
275
|
+
): Promise<void> {
|
|
276
|
+
const now = Date.now();
|
|
277
|
+
const last = this.lastSyncAt.get(parentStreamId) ?? 0;
|
|
278
|
+
if (now - last < YamlDrivenTopology.SYNC_COALESCE_MS) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
this.lastSyncAt.set(parentStreamId, now);
|
|
282
|
+
|
|
283
|
+
// Find live agents whose stream's parent matches and whose role has
|
|
284
|
+
// on_parent_advanced: sync_with_parent.
|
|
285
|
+
for (const [agentId, streamId] of this.agentStreams) {
|
|
286
|
+
const role = this.agentRoles.get(agentId);
|
|
287
|
+
if (!role) continue;
|
|
288
|
+
const roleConfig = this.getRoleConfig(role);
|
|
289
|
+
if (roleConfig?.on_parent_advanced !== 'sync_with_parent') continue;
|
|
290
|
+
|
|
291
|
+
const stream = ctx.workspaceManager.getStream(streamId);
|
|
292
|
+
if (stream?.parentStream !== parentStreamId) continue;
|
|
293
|
+
|
|
294
|
+
// Find agent's worktree
|
|
295
|
+
const worktree = ctx.workspaceManager.getWorktreeForAgent(agentId);
|
|
296
|
+
if (!worktree) continue; // no worktree → can't sync; skip silently
|
|
297
|
+
|
|
298
|
+
// Map YAML's on_conflict → git-cascade ConflictStrategy. 'defer' has
|
|
299
|
+
// no git-native equivalent; we use 'manual' (git-cascade records the
|
|
300
|
+
// conflict and leaves the worktree in a conflict state).
|
|
301
|
+
const yamlStrategy = roleConfig.on_conflict ?? 'defer';
|
|
302
|
+
const onConflict =
|
|
303
|
+
yamlStrategy === 'defer' ? 'manual' : yamlStrategy;
|
|
304
|
+
try {
|
|
305
|
+
ctx.workspaceManager.syncWithParent({
|
|
306
|
+
streamId,
|
|
307
|
+
agentId,
|
|
308
|
+
worktree: worktree.path,
|
|
309
|
+
onConflict,
|
|
310
|
+
});
|
|
311
|
+
} catch {
|
|
312
|
+
// Best-effort — don't throw out of an event handler
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace V3 Types — stream-first primitives.
|
|
3
|
+
*
|
|
4
|
+
* These types supplement the legacy role-shaped `types.ts` for the redesigned
|
|
5
|
+
* workspace layer (see `docs/workspace-interfaces.md`). Once the migration is
|
|
6
|
+
* complete (Phase 9), these merge back into `types.ts` and the V3 prefix is
|
|
7
|
+
* dropped.
|
|
8
|
+
*
|
|
9
|
+
* @module workspace/types-v3
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
Stream as GCStream,
|
|
14
|
+
AgentWorktree,
|
|
15
|
+
MergeResult as GCMergeResult,
|
|
16
|
+
RebaseResult as GCRebaseResult,
|
|
17
|
+
Change as GCChange,
|
|
18
|
+
ChangeStatus,
|
|
19
|
+
ConflictStrategy as GCConflictStrategy,
|
|
20
|
+
ConflictRecord as GCConflictRecord,
|
|
21
|
+
} from 'git-cascade';
|
|
22
|
+
|
|
23
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
24
|
+
// Identity
|
|
25
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
export type AgentId = string;
|
|
28
|
+
export type StreamId = string;
|
|
29
|
+
export type ChangeId = string;
|
|
30
|
+
export type QueueEntryId = string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Pseudo-principals own resources that aren't bound to a live agent. Team-root
|
|
34
|
+
* streams use `team:<name>`; system-created streams use `system:<subsystem>`.
|
|
35
|
+
* Tagged via prefix; never terminates.
|
|
36
|
+
*/
|
|
37
|
+
export type PseudoAgentId = `team:${string}` | `system:${string}`;
|
|
38
|
+
|
|
39
|
+
export type Principal = AgentId | PseudoAgentId;
|
|
40
|
+
|
|
41
|
+
export const isPseudoAgentId = (p: Principal): p is PseudoAgentId =>
|
|
42
|
+
p.startsWith('team:') || p.startsWith('system:');
|
|
43
|
+
|
|
44
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
45
|
+
// Streams
|
|
46
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Specification for creating a new stream. If `parent` is set, the stream
|
|
50
|
+
* forks from that parent stream. Otherwise, `forkFrom` names the branch to
|
|
51
|
+
* fork from (defaults to 'main').
|
|
52
|
+
*/
|
|
53
|
+
export interface StreamSpec {
|
|
54
|
+
name: string;
|
|
55
|
+
ownerId: Principal;
|
|
56
|
+
parent?: StreamId;
|
|
57
|
+
forkFrom?: string;
|
|
58
|
+
metadata?: Record<string, unknown>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type Stream = GCStream;
|
|
62
|
+
export type MergeResult = GCMergeResult;
|
|
63
|
+
export type RebaseResult = GCRebaseResult;
|
|
64
|
+
export type Change = GCChange;
|
|
65
|
+
export type ConflictStrategy = GCConflictStrategy;
|
|
66
|
+
export type ConflictRecord = GCConflictRecord;
|
|
67
|
+
export type { ChangeStatus };
|
|
68
|
+
|
|
69
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
70
|
+
// Worktrees (role-neutral)
|
|
71
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
export interface AllocateWorktreeOpts {
|
|
74
|
+
agentId: Principal;
|
|
75
|
+
streamId?: StreamId;
|
|
76
|
+
baseDir?: string;
|
|
77
|
+
pooled?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* If set, co-locate this agent on the referenced agent's worktree
|
|
80
|
+
* (ref-counted; last-out wins on deallocation).
|
|
81
|
+
*/
|
|
82
|
+
sharedWithAgent?: AgentId;
|
|
83
|
+
/** Optional branch override; defaults to the stream's branch. */
|
|
84
|
+
branch?: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type Worktree = AgentWorktree;
|
|
88
|
+
|
|
89
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
90
|
+
// Cascade
|
|
91
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
export type CascadeStrategy = 'stop_on_conflict' | 'skip_conflicting' | 'defer_conflicts';
|
|
94
|
+
|
|
95
|
+
export interface CascadeResult {
|
|
96
|
+
rootStreamId: StreamId;
|
|
97
|
+
succeeded: StreamId[];
|
|
98
|
+
failed: Array<{ streamId: StreamId; conflictId?: string; error?: string }>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
102
|
+
// Reconcile
|
|
103
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Result of macro-agent's reconcile wrapper — handles both git-cascade's
|
|
107
|
+
* stream↔git sync and macro-level worktree/pool state.
|
|
108
|
+
*/
|
|
109
|
+
export interface MacroReconcileResult {
|
|
110
|
+
streamsChecked: number;
|
|
111
|
+
streamsFixed: number;
|
|
112
|
+
worktreesOrphaned: number;
|
|
113
|
+
worktreesCleaned: number;
|
|
114
|
+
poolEntriesPurged: number;
|
|
115
|
+
errors: Array<{ context: string; message: string }>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
119
|
+
// Landing (interface only — strategies in Phase 5)
|
|
120
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
export interface LandingContext {
|
|
123
|
+
agentId: AgentId;
|
|
124
|
+
streamId: StreamId;
|
|
125
|
+
sourceWorktree: string;
|
|
126
|
+
targetStreamId?: StreamId;
|
|
127
|
+
/**
|
|
128
|
+
* Strategy selector. Accepts either an internal strategy name
|
|
129
|
+
* (`merge-to-parent`, `queue-to-branch`, …) or the YAML form
|
|
130
|
+
* (`merge_to_parent_stream`, `queue_to_branch`, …). `WorkspaceManager.land`
|
|
131
|
+
* normalizes. When undefined, `merge-to-parent` is used.
|
|
132
|
+
*/
|
|
133
|
+
strategyName?: string;
|
|
134
|
+
strategyConfig?: Record<string, unknown>;
|
|
135
|
+
/** Reference to the manager; strategies call back for merge/cascade. */
|
|
136
|
+
workspaceManager: unknown; // WorkspaceManager — circular; narrowed at callsite
|
|
137
|
+
/**
|
|
138
|
+
* Optional task reference inherited from the spawning agent. Strategies
|
|
139
|
+
* that produce commits/merges should thread this into their adapter
|
|
140
|
+
* calls' metadata as `{ task_ref }` so the resulting cascade events carry
|
|
141
|
+
* the binding.
|
|
142
|
+
*/
|
|
143
|
+
taskRef?: import('git-cascade/events').TaskRef;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface LandingStrategy {
|
|
147
|
+
readonly name: string;
|
|
148
|
+
canLand?(ctx: LandingContext): boolean;
|
|
149
|
+
land(ctx: LandingContext): Promise<MergeResult>;
|
|
150
|
+
initialize?(): Promise<void>;
|
|
151
|
+
close?(): Promise<void>;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
155
|
+
// Helpers
|
|
156
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Opaque branded marker — use for inferring that something is a v3 concept.
|
|
160
|
+
* Only used in documentation / type exports; no runtime effect.
|
|
161
|
+
*/
|
|
162
|
+
export const V3_MARKER: unique symbol = Symbol('workspace.v3');
|