@copilot-swarm/core 0.0.13 → 0.0.16

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.
Files changed (56) hide show
  1. package/defaults/agents/cross-model-reviewer.md +2 -1
  2. package/dist/analysis-engine.d.ts.map +1 -1
  3. package/dist/analysis-engine.js +6 -2
  4. package/dist/analysis-engine.js.map +1 -1
  5. package/dist/checkpoint.d.ts +9 -0
  6. package/dist/checkpoint.d.ts.map +1 -1
  7. package/dist/checkpoint.js.map +1 -1
  8. package/dist/checkpoint.test.js +1 -0
  9. package/dist/checkpoint.test.js.map +1 -1
  10. package/dist/config.d.ts +5 -1
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +19 -2
  13. package/dist/config.js.map +1 -1
  14. package/dist/finish.d.ts +32 -0
  15. package/dist/finish.d.ts.map +1 -0
  16. package/dist/finish.js +232 -0
  17. package/dist/finish.js.map +1 -0
  18. package/dist/index.js +81 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/messages.d.ts +8 -0
  21. package/dist/messages.d.ts.map +1 -1
  22. package/dist/messages.js +9 -0
  23. package/dist/messages.js.map +1 -1
  24. package/dist/orchestrator.d.ts.map +1 -1
  25. package/dist/orchestrator.js +2 -0
  26. package/dist/orchestrator.js.map +1 -1
  27. package/dist/paths.d.ts +6 -6
  28. package/dist/paths.d.ts.map +1 -1
  29. package/dist/paths.js +17 -10
  30. package/dist/paths.js.map +1 -1
  31. package/dist/pipeline-engine.d.ts.map +1 -1
  32. package/dist/pipeline-engine.js +32 -17
  33. package/dist/pipeline-engine.js.map +1 -1
  34. package/dist/planning-engine.d.ts +5 -0
  35. package/dist/planning-engine.d.ts.map +1 -1
  36. package/dist/planning-engine.js +78 -22
  37. package/dist/planning-engine.js.map +1 -1
  38. package/dist/progress-tracker.d.ts +2 -0
  39. package/dist/progress-tracker.d.ts.map +1 -1
  40. package/dist/progress-tracker.js +2 -0
  41. package/dist/progress-tracker.js.map +1 -1
  42. package/dist/session-store.d.ts +38 -0
  43. package/dist/session-store.d.ts.map +1 -0
  44. package/dist/session-store.js +158 -0
  45. package/dist/session-store.js.map +1 -0
  46. package/dist/session.d.ts +9 -3
  47. package/dist/session.d.ts.map +1 -1
  48. package/dist/session.js +20 -4
  49. package/dist/session.js.map +1 -1
  50. package/dist/textarea.d.ts.map +1 -1
  51. package/dist/textarea.js +119 -36
  52. package/dist/textarea.js.map +1 -1
  53. package/dist/tui-renderer.d.ts.map +1 -1
  54. package/dist/tui-renderer.js +2 -1
  55. package/dist/tui-renderer.js.map +1 -1
  56. package/package.json +1 -1
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Session management for grouping related runs (analyze, plan, run, review)
3
+ * under a single logical feature/project.
4
+ *
5
+ * Structure:
6
+ * .swarm/
7
+ * sessions/
8
+ * <sessionId>/
9
+ * session.json # metadata
10
+ * runs/<runId>/ # run directories (same layout as before)
11
+ * plans/ # plan outputs
12
+ * analysis/ # analysis outputs
13
+ * latest # latest run pointer within session
14
+ * active-session # pointer to the active session ID
15
+ */
16
+ import * as fs from "node:fs/promises";
17
+ import * as path from "node:path";
18
+ const SESSIONS_DIR = "sessions";
19
+ const ACTIVE_SESSION_FILE = "active-session";
20
+ function sessionsRoot(config) {
21
+ return path.join(config.repoRoot, config.swarmDir, SESSIONS_DIR);
22
+ }
23
+ function activeSessionPath(config) {
24
+ return path.join(config.repoRoot, config.swarmDir, ACTIVE_SESSION_FILE);
25
+ }
26
+ function sessionDir(config, sessionId) {
27
+ return path.join(sessionsRoot(config), sessionId);
28
+ }
29
+ function sessionJsonPath(config, sessionId) {
30
+ return path.join(sessionDir(config, sessionId), "session.json");
31
+ }
32
+ /** Create a new session and set it as active. */
33
+ export async function createSession(config, name, description) {
34
+ const id = generateSessionId();
35
+ const session = {
36
+ id,
37
+ name,
38
+ created: new Date().toISOString(),
39
+ description,
40
+ };
41
+ const dir = sessionDir(config, id);
42
+ await fs.mkdir(dir, { recursive: true });
43
+ await fs.writeFile(sessionJsonPath(config, id), JSON.stringify(session, null, 2));
44
+ // Set as active
45
+ await setActiveSession(config, id);
46
+ return session;
47
+ }
48
+ /** List all sessions, sorted by creation date (newest first). */
49
+ export async function listSessions(config) {
50
+ const root = sessionsRoot(config);
51
+ try {
52
+ const entries = await fs.readdir(root, { withFileTypes: true });
53
+ const sessions = [];
54
+ for (const entry of entries) {
55
+ if (!entry.isDirectory())
56
+ continue;
57
+ const jsonPath = path.join(root, entry.name, "session.json");
58
+ try {
59
+ const content = await fs.readFile(jsonPath, "utf-8");
60
+ sessions.push(JSON.parse(content));
61
+ }
62
+ catch {
63
+ // Skip invalid session directories
64
+ }
65
+ }
66
+ sessions.sort((a, b) => b.created.localeCompare(a.created));
67
+ return sessions;
68
+ }
69
+ catch {
70
+ return [];
71
+ }
72
+ }
73
+ /** Get a session by ID. */
74
+ export async function getSession(config, sessionId) {
75
+ try {
76
+ const content = await fs.readFile(sessionJsonPath(config, sessionId), "utf-8");
77
+ return JSON.parse(content);
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ }
83
+ /** Get the active session ID. */
84
+ export async function getActiveSessionId(config) {
85
+ try {
86
+ return (await fs.readFile(activeSessionPath(config), "utf-8")).trim();
87
+ }
88
+ catch {
89
+ return null;
90
+ }
91
+ }
92
+ /** Set the active session. */
93
+ export async function setActiveSession(config, sessionId) {
94
+ const root = path.join(config.repoRoot, config.swarmDir);
95
+ await fs.mkdir(root, { recursive: true });
96
+ await fs.writeFile(activeSessionPath(config), sessionId);
97
+ }
98
+ /**
99
+ * Resolve the effective session ID for the current command.
100
+ * Priority: CLI --session flag > active-session file > auto-create default.
101
+ */
102
+ export async function resolveSessionId(config) {
103
+ // 1. Explicit CLI flag
104
+ if (config.sessionId) {
105
+ const session = await getSession(config, config.sessionId);
106
+ if (!session) {
107
+ throw new Error(`Session not found: ${config.sessionId}`);
108
+ }
109
+ return config.sessionId;
110
+ }
111
+ // 2. Active session pointer
112
+ const activeId = await getActiveSessionId(config);
113
+ if (activeId) {
114
+ const session = await getSession(config, activeId);
115
+ if (session)
116
+ return activeId;
117
+ }
118
+ // 3. Auto-create a default session, migrating legacy runs if present
119
+ return migrateOrCreateDefault(config);
120
+ }
121
+ /**
122
+ * If legacy .swarm/runs/ exists, migrate into a "default" session.
123
+ * Otherwise, create a fresh default session.
124
+ */
125
+ async function migrateOrCreateDefault(config) {
126
+ const swarmRootPath = path.join(config.repoRoot, config.swarmDir);
127
+ const legacyRunsDir = path.join(swarmRootPath, "runs");
128
+ const legacyPlansDir = path.join(swarmRootPath, "plans");
129
+ const legacyAnalysisDir = path.join(swarmRootPath, "analysis");
130
+ const legacyLatest = path.join(swarmRootPath, "latest");
131
+ const session = await createSession(config, "default", "Auto-created default session");
132
+ // Migrate legacy directories if they exist
133
+ const sDir = sessionDir(config, session.id);
134
+ for (const [src, dest] of [
135
+ [legacyRunsDir, path.join(sDir, "runs")],
136
+ [legacyPlansDir, path.join(sDir, "plans")],
137
+ [legacyAnalysisDir, path.join(sDir, "analysis")],
138
+ [legacyLatest, path.join(sDir, "latest")],
139
+ ]) {
140
+ try {
141
+ await fs.access(src);
142
+ await fs.rename(src, dest);
143
+ }
144
+ catch {
145
+ // Source doesn't exist — skip
146
+ }
147
+ }
148
+ return session.id;
149
+ }
150
+ function generateSessionId() {
151
+ // Short readable ID: 8 hex chars
152
+ const bytes = new Uint8Array(4);
153
+ crypto.getRandomValues(bytes);
154
+ return Array.from(bytes)
155
+ .map((b) => b.toString(16).padStart(2, "0"))
156
+ .join("");
157
+ }
158
+ //# sourceMappingURL=session-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-store.js","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAUlC,MAAM,YAAY,GAAG,UAAU,CAAC;AAChC,MAAM,mBAAmB,GAAG,gBAAgB,CAAC;AAE7C,SAAS,YAAY,CAAC,MAAmB;IACvC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAmB;IAC5C,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;AAC1E,CAAC;AAED,SAAS,UAAU,CAAC,MAAmB,EAAE,SAAiB;IACxD,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,eAAe,CAAC,MAAmB,EAAE,SAAiB;IAC7D,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,cAAc,CAAC,CAAC;AAClE,CAAC;AAED,iDAAiD;AACjD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAmB,EAAE,IAAY,EAAE,WAAoB;IACzF,MAAM,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAC/B,MAAM,OAAO,GAAiB;QAC5B,EAAE;QACF,IAAI;QACJ,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACjC,WAAW;KACZ,CAAC;IAEF,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACnC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,EAAE,CAAC,SAAS,CAAC,eAAe,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAElF,gBAAgB;IAChB,MAAM,gBAAgB,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAEnC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAmB;IACpD,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,MAAM,QAAQ,GAAmB,EAAE,CAAC;QACpC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;gBAAE,SAAS;YACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;YAC7D,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBACrD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAiB,CAAC,CAAC;YACrD,CAAC;YAAC,MAAM,CAAC;gBACP,mCAAmC;YACrC,CAAC;QACH,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAC5D,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,2BAA2B;AAC3B,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAmB,EAAE,SAAiB;IACrE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC;QAC/E,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAiB,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,iCAAiC;AACjC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAmB;IAC1D,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,8BAA8B;AAC9B,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAmB,EAAE,SAAiB;IAC3E,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzD,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,EAAE,CAAC,SAAS,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC;AAC3D,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAmB;IACxD,uBAAuB;IACvB,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrB,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;QAC3D,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,sBAAsB,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,MAAM,CAAC,SAAS,CAAC;IAC1B,CAAC;IAED,4BAA4B;IAC5B,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAClD,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACnD,IAAI,OAAO;YAAE,OAAO,QAAQ,CAAC;IAC/B,CAAC;IAED,qEAAqE;IACrE,OAAO,sBAAsB,CAAC,MAAM,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,sBAAsB,CAAC,MAAmB;IACvD,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAClE,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IACvD,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;IAC/D,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IAExD,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,8BAA8B,CAAC,CAAC;IAEvF,2CAA2C;IAC3C,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5C,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI;QACxB,CAAC,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACxC,CAAC,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC1C,CAAC,iBAAiB,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAChD,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;KAC1C,EAAE,CAAC;QACF,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACrB,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,8BAA8B;QAChC,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,EAAE,CAAC;AACpB,CAAC;AAED,SAAS,iBAAiB;IACxB,iCAAiC;IACjC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;IAChC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC9B,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;SACrB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC"}
package/dist/session.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { CopilotSession } from "@github/copilot-sdk";
2
+ import type { SessionRecord } from "./checkpoint.js";
2
3
  import type { SwarmConfig } from "./config.js";
3
4
  import type { Logger } from "./logger.js";
4
5
  import type { PipelineConfig } from "./pipeline-types.js";
@@ -8,7 +9,12 @@ export declare class SessionManager {
8
9
  private readonly logger;
9
10
  private readonly client;
10
11
  private readonly instructionCache;
12
+ private readonly _sessionLog;
11
13
  constructor(config: SwarmConfig, pipeline: PipelineConfig, logger: Logger);
14
+ /** All Copilot SDK sessions created during this run, keyed by context. */
15
+ get sessionLog(): Record<string, SessionRecord>;
16
+ /** Record a session with a context key (e.g. "spec-0", "implement-3/stream-1"). */
17
+ recordSession(key: string, session: CopilotSession, agent: string, role: string): void;
12
18
  start(): Promise<void>;
13
19
  stop(): Promise<void>;
14
20
  /**
@@ -18,10 +24,10 @@ export declare class SessionManager {
18
24
  * - Undefined → fall back to `<agentsDir>/<agentName>.md`
19
25
  */
20
26
  loadAgentInstructions(agentName: string): Promise<string>;
21
- createAgentSession(agentName: string, model?: string): Promise<CopilotSession>;
27
+ createAgentSession(agentName: string, model?: string, sessionKey?: string): Promise<CopilotSession>;
22
28
  createSessionWithInstructions(instructions: string, model?: string): Promise<CopilotSession>;
23
29
  send(session: CopilotSession, prompt: string, spinnerLabel?: string): Promise<string>;
24
- callIsolated(agentName: string, prompt: string, model?: string): Promise<string>;
25
- callIsolatedWithInstructions(instructions: string, prompt: string, spinnerLabel: string, model?: string): Promise<string>;
30
+ callIsolated(agentName: string, prompt: string, model?: string, sessionKey?: string): Promise<string>;
31
+ callIsolatedWithInstructions(instructions: string, prompt: string, spinnerLabel: string, model?: string, sessionKey?: string): Promise<string>;
26
32
  }
27
33
  //# sourceMappingURL=session.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAK1D,qBAAa,cAAc;IAKvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IANzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;gBAG3C,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,MAAM;IAO3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;;;;OAKG;IACG,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA6CzD,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAK9E,6BAA6B,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAqB5F,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQrF,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuBhF,4BAA4B,CAChC,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC;CAsBnB"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAK1D,qBAAa,cAAc;IAMvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;IAC9D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqC;gBAG9C,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,MAAM;IAOjC,0EAA0E;IAC1E,IAAI,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAE9C;IAED,mFAAmF;IACnF,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAIhF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;;;;OAKG;IACG,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA6CzD,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAOnG,6BAA6B,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAqB5F,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQrF,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAwBrG,4BAA4B,CAChC,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,KAAK,CAAC,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC;CAuBnB"}
package/dist/session.js CHANGED
@@ -12,6 +12,7 @@ export class SessionManager {
12
12
  logger;
13
13
  client;
14
14
  instructionCache = new Map();
15
+ _sessionLog = {};
15
16
  constructor(config, pipeline, logger) {
16
17
  this.config = config;
17
18
  this.pipeline = pipeline;
@@ -20,6 +21,14 @@ export class SessionManager {
20
21
  logLevel: config.verbose ? "debug" : "warning",
21
22
  });
22
23
  }
24
+ /** All Copilot SDK sessions created during this run, keyed by context. */
25
+ get sessionLog() {
26
+ return this._sessionLog;
27
+ }
28
+ /** Record a session with a context key (e.g. "spec-0", "implement-3/stream-1"). */
29
+ recordSession(key, session, agent, role) {
30
+ this._sessionLog[key] = { sessionId: session.sessionId, agent, role };
31
+ }
23
32
  async start() {
24
33
  await this.client.start();
25
34
  }
@@ -74,9 +83,12 @@ export class SessionManager {
74
83
  }
75
84
  throw new Error(`Failed to load agent instructions for "${agentName}": not found in repo (${repoFilePath}) or bundled defaults`);
76
85
  }
77
- async createAgentSession(agentName, model) {
86
+ async createAgentSession(agentName, model, sessionKey) {
78
87
  const instructions = await this.loadAgentInstructions(agentName);
79
- return this.createSessionWithInstructions(instructions, model);
88
+ const session = await this.createSessionWithInstructions(instructions, model);
89
+ if (sessionKey)
90
+ this.recordSession(sessionKey, session, agentName, agentName);
91
+ return session;
80
92
  }
81
93
  async createSessionWithInstructions(instructions, model) {
82
94
  const session = await this.client.createSession({
@@ -104,10 +116,12 @@ export class SessionManager {
104
116
  this.logger.newline();
105
117
  return response?.data.content ?? "";
106
118
  }
107
- async callIsolated(agentName, prompt, model) {
119
+ async callIsolated(agentName, prompt, model, sessionKey) {
108
120
  const maxAttempts = this.config.maxRetries;
109
121
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
110
122
  const session = await this.createAgentSession(agentName, model);
123
+ if (sessionKey)
124
+ this.recordSession(sessionKey, session, agentName, agentName);
111
125
  try {
112
126
  const content = await this.send(session, prompt, `${agentName} is working…`);
113
127
  if (!content && attempt < maxAttempts) {
@@ -128,10 +142,12 @@ export class SessionManager {
128
142
  }
129
143
  return "";
130
144
  }
131
- async callIsolatedWithInstructions(instructions, prompt, spinnerLabel, model) {
145
+ async callIsolatedWithInstructions(instructions, prompt, spinnerLabel, model, sessionKey) {
132
146
  const maxAttempts = this.config.maxRetries;
133
147
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
134
148
  const session = await this.createSessionWithInstructions(instructions, model);
149
+ if (sessionKey)
150
+ this.recordSession(sessionKey, session, "inline-agent", "inline-agent");
135
151
  try {
136
152
  const content = await this.send(session, prompt, spinnerLabel);
137
153
  if (!content && attempt < maxAttempts) {
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"textarea.d.ts","sourceRoot":"","sources":["../src/textarea.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAuNH,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAiKhE;AAID,MAAM,WAAW,kBAAkB;IACjC,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA6RpH"}
1
+ {"version":3,"file":"textarea.d.ts","sourceRoot":"","sources":["../src/textarea.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAkRH,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAkLhE;AAID,MAAM,WAAW,kBAAkB;IACjC,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA8SpH"}
package/dist/textarea.js CHANGED
@@ -62,22 +62,64 @@ function wrapText(text, width) {
62
62
  return [""];
63
63
  const result = [];
64
64
  for (const rawLine of text.split("\n")) {
65
- if (rawLine.length <= width) {
66
- result.push(rawLine);
67
- continue;
68
- }
69
- let remaining = rawLine;
70
- while (remaining.length > width) {
71
- let cut = remaining.lastIndexOf(" ", width);
72
- if (cut <= 0)
73
- cut = width;
74
- result.push(remaining.substring(0, cut));
75
- remaining = remaining.substring(cut).trimStart();
76
- }
77
- result.push(remaining);
65
+ for (const vr of wrapLine(rawLine, width))
66
+ result.push(vr);
78
67
  }
79
68
  return result;
80
69
  }
70
+ /** Wrap a single line into visual rows at word boundaries. */
71
+ function wrapLine(line, width) {
72
+ if (width <= 0)
73
+ return [line];
74
+ if (line.length <= width)
75
+ return [line];
76
+ const rows = [];
77
+ let remaining = line;
78
+ while (remaining.length > width) {
79
+ let cut = remaining.lastIndexOf(" ", width);
80
+ if (cut <= 0)
81
+ cut = width;
82
+ rows.push(remaining.substring(0, cut));
83
+ remaining = remaining.substring(cut === width ? cut : cut + 1);
84
+ }
85
+ rows.push(remaining);
86
+ return rows;
87
+ }
88
+ /**
89
+ * Compute the visual row count for a set of lines at a given width.
90
+ * Also builds a map from logical line index to starting visual row.
91
+ */
92
+ function computeVisualLayout(lines, width) {
93
+ const lineStartRow = [];
94
+ const wrappedLines = [];
95
+ let total = 0;
96
+ for (const line of lines) {
97
+ lineStartRow.push(total);
98
+ const wrapped = wrapLine(line, width);
99
+ wrappedLines.push(wrapped);
100
+ total += wrapped.length;
101
+ }
102
+ return { totalVisualRows: total, lineStartRow, wrappedLines };
103
+ }
104
+ /**
105
+ * Convert a logical cursor position (row, col) to a visual row offset and
106
+ * visual column within the wrapped layout.
107
+ */
108
+ function logicalToVisual(lineStartRow, wrappedLines, logRow, logCol) {
109
+ const wrapped = wrappedLines[logRow];
110
+ let remaining = logCol;
111
+ for (let i = 0; i < wrapped.length; i++) {
112
+ const rowLen = wrapped[i].length;
113
+ // Account for removed space at word-wrap boundary
114
+ const isLastWrapRow = i === wrapped.length - 1;
115
+ if (remaining <= rowLen || isLastWrapRow) {
116
+ return { visualRow: lineStartRow[logRow] + i, visualCol: remaining };
117
+ }
118
+ // +1 for the space that was consumed as the wrap point
119
+ remaining -= rowLen + 1;
120
+ }
121
+ return { visualRow: lineStartRow[logRow], visualCol: logCol };
122
+ }
81
123
  function renderMenuOverlay(items, selectedIdx, rows, cols) {
82
124
  process.stdout.write("\x1b[?25l");
83
125
  const boxW = 30;
@@ -143,6 +185,15 @@ function handleEditorKey(ch, i, data, state) {
143
185
  state.curCol = r.col;
144
186
  }
145
187
  }
188
+ // Shift+Enter (ESC[1;2B) → insert newline
189
+ if (mod === 50 && dir === 66) {
190
+ const after = state.lines[state.curRow].substring(state.curCol);
191
+ state.lines[state.curRow] = state.lines[state.curRow].substring(0, state.curCol);
192
+ state.lines.splice(state.curRow + 1, 0, after);
193
+ state.curRow++;
194
+ state.curCol = 0;
195
+ return { handled: true, newI: i + 5, needsFullRender: true, needsCursorUpdate: false };
196
+ }
146
197
  return { handled: true, newI: i + 5, needsFullRender: false, needsCursorUpdate: true };
147
198
  }
148
199
  if (code === 65)
@@ -210,13 +261,16 @@ export async function openTextarea() {
210
261
  const innerWidth = cols - 4;
211
262
  const title = " Task Description ";
212
263
  const state = { lines: [""], curRow: 0, curCol: 0 };
213
- let scroll = 0;
264
+ let scroll = 0; // scroll in visual rows
214
265
  let menuOpen = false;
215
266
  let menuIdx = 0;
216
267
  const menuItems = [
217
268
  { label: "Submit", value: "submit" },
218
269
  { label: "Cancel", value: "cancel" },
219
270
  ];
271
+ function getLayout() {
272
+ return computeVisualLayout(state.lines, innerWidth);
273
+ }
220
274
  function clamp() {
221
275
  if (state.curRow < 0)
222
276
  state.curRow = 0;
@@ -226,13 +280,25 @@ export async function openTextarea() {
226
280
  state.curCol = 0;
227
281
  if (state.curCol > state.lines[state.curRow].length)
228
282
  state.curCol = state.lines[state.curRow].length;
229
- if (state.curRow < scroll)
230
- scroll = state.curRow;
231
- if (state.curRow >= scroll + editorHeight)
232
- scroll = state.curRow - editorHeight + 1;
283
+ const layout = getLayout();
284
+ const vis = logicalToVisual(layout.lineStartRow, layout.wrappedLines, state.curRow, state.curCol);
285
+ if (vis.visualRow < scroll)
286
+ scroll = vis.visualRow;
287
+ if (vis.visualRow >= scroll + editorHeight)
288
+ scroll = vis.visualRow - editorHeight + 1;
233
289
  }
234
- function renderLine(screenRow, lineIdx) {
235
- const text = lineIdx < state.lines.length ? state.lines[lineIdx] : "";
290
+ function renderVisualRow(screenRow, visualRowIdx) {
291
+ const layout = getLayout();
292
+ // Find which logical line + wrap row this visual row belongs to
293
+ let text = "";
294
+ for (let li = 0; li < state.lines.length; li++) {
295
+ const startVR = layout.lineStartRow[li];
296
+ const wrapped = layout.wrappedLines[li];
297
+ if (visualRowIdx >= startVR && visualRowIdx < startVR + wrapped.length) {
298
+ text = wrapped[visualRowIdx - startVR];
299
+ break;
300
+ }
301
+ }
236
302
  const display = text.substring(0, innerWidth);
237
303
  const pad = Math.max(0, innerWidth - display.length);
238
304
  process.stdout.write(`\x1b[${screenRow};1H\x1b[2K\u2502 ${display}${" ".repeat(pad)} \u2502`);
@@ -245,14 +311,16 @@ export async function openTextarea() {
245
311
  process.stdout.write("\x1b[?25l");
246
312
  process.stdout.write(`\x1b[1;1H\x1b[2K${top}`);
247
313
  for (let i = 0; i < editorHeight; i++)
248
- renderLine(2 + i, scroll + i);
314
+ renderVisualRow(2 + i, scroll + i);
249
315
  process.stdout.write(`\x1b[${2 + editorHeight};1H\x1b[2K${bottom}`);
250
316
  placeCursor();
251
317
  process.stdout.write("\x1b[?25h");
252
318
  }
253
319
  function placeCursor() {
254
- const screenRow = 2 + (state.curRow - scroll);
255
- const screenCol = 3 + Math.min(state.curCol, innerWidth);
320
+ const layout = getLayout();
321
+ const vis = logicalToVisual(layout.lineStartRow, layout.wrappedLines, state.curRow, state.curCol);
322
+ const screenRow = 2 + (vis.visualRow - scroll);
323
+ const screenCol = 3 + Math.min(vis.visualCol, innerWidth);
256
324
  process.stdout.write(`\x1b[${screenRow};${screenCol}H`);
257
325
  }
258
326
  return new Promise((resolve) => {
@@ -347,8 +415,7 @@ export async function openTextarea() {
347
415
  state.lines[state.curRow].substring(state.curCol);
348
416
  state.curCol++;
349
417
  clamp();
350
- renderLine(2 + (state.curRow - scroll), state.curRow);
351
- placeCursor();
418
+ needsFullRender = true;
352
419
  }
353
420
  }
354
421
  if (needsFullRender)
@@ -387,7 +454,7 @@ export async function openSplitEditor(contextText, options) {
387
454
  const contextTitle = ` ${options?.contextTitle ?? "Agent Questions"} `;
388
455
  // Editor state (left panel)
389
456
  const state = { lines: [""], curRow: 0, curCol: 0 };
390
- let edScroll = 0;
457
+ let edScroll = 0; // scroll in visual rows
391
458
  // Context state (right panel)
392
459
  const ctxLines = wrapText(contextText, rightInner);
393
460
  let ctxScroll = 0;
@@ -400,6 +467,9 @@ export async function openSplitEditor(contextText, options) {
400
467
  { label: "Skip (use AI judgment)", value: "skip" },
401
468
  { label: "Cancel", value: "cancel" },
402
469
  ];
470
+ function getEdLayout() {
471
+ return computeVisualLayout(state.lines, leftInner);
472
+ }
403
473
  function clampEditor() {
404
474
  if (state.curRow < 0)
405
475
  state.curRow = 0;
@@ -409,10 +479,12 @@ export async function openSplitEditor(contextText, options) {
409
479
  state.curCol = 0;
410
480
  if (state.curCol > state.lines[state.curRow].length)
411
481
  state.curCol = state.lines[state.curRow].length;
412
- if (state.curRow < edScroll)
413
- edScroll = state.curRow;
414
- if (state.curRow >= edScroll + bodyHeight)
415
- edScroll = state.curRow - bodyHeight + 1;
482
+ const layout = getEdLayout();
483
+ const vis = logicalToVisual(layout.lineStartRow, layout.wrappedLines, state.curRow, state.curCol);
484
+ if (vis.visualRow < edScroll)
485
+ edScroll = vis.visualRow;
486
+ if (vis.visualRow >= edScroll + bodyHeight)
487
+ edScroll = vis.visualRow - bodyHeight + 1;
416
488
  }
417
489
  function clampCtx() {
418
490
  if (ctxScroll < 0)
@@ -421,8 +493,18 @@ export async function openSplitEditor(contextText, options) {
421
493
  ctxScroll = ctxMaxScroll;
422
494
  }
423
495
  function renderRow(screenRow, bodyIdx) {
424
- const edLineIdx = edScroll + bodyIdx;
425
- const edText = edLineIdx < state.lines.length ? state.lines[edLineIdx] : "";
496
+ // Left panel: get visual row from wrapped editor layout
497
+ const edVisIdx = edScroll + bodyIdx;
498
+ const layout = getEdLayout();
499
+ let edText = "";
500
+ for (let li = 0; li < state.lines.length; li++) {
501
+ const startVR = layout.lineStartRow[li];
502
+ const wrapped = layout.wrappedLines[li];
503
+ if (edVisIdx >= startVR && edVisIdx < startVR + wrapped.length) {
504
+ edText = wrapped[edVisIdx - startVR];
505
+ break;
506
+ }
507
+ }
426
508
  const edDisplay = edText.substring(0, leftInner);
427
509
  const edPad = Math.max(0, leftInner - edDisplay.length);
428
510
  const leftDim = focus === "right" ? "\x1b[2m" : "";
@@ -462,8 +544,10 @@ export async function openSplitEditor(contextText, options) {
462
544
  }
463
545
  function placeCursorSplit() {
464
546
  if (focus === "left") {
465
- const screenRow = 2 + (state.curRow - edScroll);
466
- const screenCol = 3 + Math.min(state.curCol, leftInner);
547
+ const layout = getEdLayout();
548
+ const vis = logicalToVisual(layout.lineStartRow, layout.wrappedLines, state.curRow, state.curCol);
549
+ const screenRow = 2 + (vis.visualRow - edScroll);
550
+ const screenCol = 3 + Math.min(vis.visualCol, leftInner);
467
551
  process.stdout.write(`\x1b[?25h\x1b[${screenRow};${screenCol}H`);
468
552
  }
469
553
  else {
@@ -632,8 +716,7 @@ export async function openSplitEditor(contextText, options) {
632
716
  state.lines[state.curRow].substring(state.curCol);
633
717
  state.curCol++;
634
718
  clampEditor();
635
- renderRow(2 + (state.curRow - edScroll), state.curRow - edScroll);
636
- placeCursorSplit();
719
+ needsFullRender = true;
637
720
  }
638
721
  }
639
722
  if (needsFullRender)