@a5c-ai/babysitter-sdk 0.0.183-staging.08ae538f → 0.0.183-staging.0a3fc67d

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.
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/harness/discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAO9F,0DAA0D;AAC1D,UAAU,WAAW;IACnB,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;OAKG;IACH,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,+CAA+C;IAC/C,YAAY,EAAE,iBAAiB,EAAE,CAAC;CACnC;AAMD,mDAAmD;AACnD,eAAO,MAAM,eAAe,EAAE,SAAS,WAAW,EA2CxC,CAAC;AAyCX;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA0BlE;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAuB3E;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,IAAI,mBAAmB,GAAG,IAAI,CAahE"}
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/harness/discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAO9F,0DAA0D;AAC1D,UAAU,WAAW;IACnB,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;OAKG;IACH,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,+CAA+C;IAC/C,YAAY,EAAE,iBAAiB,EAAE,CAAC;CACnC;AAMD,mDAAmD;AACnD,eAAO,MAAM,eAAe,EAAE,SAAS,WAAW,EAoDxC,CAAC;AA0CX;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA0BlE;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAuB3E;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,IAAI,mBAAmB,GAAG,IAAI,CAahE"}
@@ -86,6 +86,15 @@ exports.KNOWN_HARNESSES = [
86
86
  callerEnvVars: ["GEMINI_SESSION_ID", "GEMINI_PROJECT_DIR", "GEMINI_CWD"],
87
87
  capabilities: [types_1.HarnessCapability.SessionBinding, types_1.HarnessCapability.HeadlessPrompt],
88
88
  },
89
+ {
90
+ name: "github-copilot",
91
+ cli: "copilot",
92
+ // Note: official Copilot docs do not confirm these env vars are injected
93
+ // into hooks. They are used as best-effort discriminators for caller detection.
94
+ callerEnvVars: ["COPILOT_HOME", "COPILOT_GITHUB_TOKEN"],
95
+ // No StopHook — Copilot CLI uses in-turn orchestration model
96
+ capabilities: [types_1.HarnessCapability.HeadlessPrompt, types_1.HarnessCapability.SessionBinding, types_1.HarnessCapability.Mcp],
97
+ },
89
98
  {
90
99
  name: "opencode",
91
100
  cli: "opencode",
@@ -115,6 +124,7 @@ const CONFIG_PATHS = {
115
124
  pi: [".pi"],
116
125
  "oh-my-pi": [".pi"],
117
126
  "gemini-cli": [".gemini"],
127
+ "github-copilot": [".copilot", ".github"],
118
128
  cursor: [".cursor", ".cursorrules"],
119
129
  opencode: [".opencode"],
120
130
  };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * GitHub Copilot CLI harness adapter.
3
+ *
4
+ * Centralizes all GitHub Copilot CLI-specific behaviors:
5
+ * - Session ID resolution (hook stdin JSON with `timestamp` and `cwd`)
6
+ * - State directory conventions (.a5c/state/ by default)
7
+ * - Plugin root resolution (COPILOT_PLUGIN_ROOT)
8
+ * - Session binding (run:create → state file with run association)
9
+ * - Session-end hook handler (cleanup on session termination)
10
+ * - Session-start hook handler (baseline state file)
11
+ *
12
+ * GitHub Copilot CLI Hook Protocol (hooks.json):
13
+ * - Hook event types (camelCase): sessionStart, sessionEnd,
14
+ * userPromptSubmitted, preToolUse, postToolUse, errorOccurred
15
+ * - Input: JSON via stdin (fields: timestamp, cwd)
16
+ * - Output: JSON via stdout
17
+ * - Only preToolUse can control flow via
18
+ * `{"permissionDecision": "deny|allow|ask", "permissionDecisionReason": "..."}`
19
+ * - ALL OTHER hook outputs are IGNORED by the Copilot CLI
20
+ *
21
+ * Orchestration model: in-turn
22
+ * GitHub Copilot CLI does NOT have a stop-hook that can block/restart the
23
+ * agent. The orchestration loop is driven by the agent itself within a
24
+ * single session (in-turn model), not by hook-driven re-entry.
25
+ *
26
+ * CRITICAL: isActive() detection — the official docs do NOT confirm any
27
+ * specific env vars (COPILOT_HOME, COPILOT_GITHUB_TOKEN, etc.) being
28
+ * injected into hook scripts. Hooks receive stdin JSON with `timestamp`
29
+ * and `cwd`. Custom env vars can be set via the `env` field in hooks.json.
30
+ * We use COPILOT_HOME / COPILOT_GITHUB_TOKEN as best-effort discriminators
31
+ * when the CLI is running, but this may need refinement.
32
+ */
33
+ import type { HarnessAdapter } from "./types";
34
+ export declare function createGithubCopilotAdapter(): HarnessAdapter;
35
+ //# sourceMappingURL=githubCopilot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"githubCopilot.d.ts","sourceRoot":"","sources":["../../src/harness/githubCopilot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAeH,OAAO,KAAK,EACV,cAAc,EAMf,MAAM,SAAS,CAAC;AAumBjB,wBAAgB,0BAA0B,IAAI,cAAc,CAqG3D"}
@@ -0,0 +1,629 @@
1
+ "use strict";
2
+ /**
3
+ * GitHub Copilot CLI harness adapter.
4
+ *
5
+ * Centralizes all GitHub Copilot CLI-specific behaviors:
6
+ * - Session ID resolution (hook stdin JSON with `timestamp` and `cwd`)
7
+ * - State directory conventions (.a5c/state/ by default)
8
+ * - Plugin root resolution (COPILOT_PLUGIN_ROOT)
9
+ * - Session binding (run:create → state file with run association)
10
+ * - Session-end hook handler (cleanup on session termination)
11
+ * - Session-start hook handler (baseline state file)
12
+ *
13
+ * GitHub Copilot CLI Hook Protocol (hooks.json):
14
+ * - Hook event types (camelCase): sessionStart, sessionEnd,
15
+ * userPromptSubmitted, preToolUse, postToolUse, errorOccurred
16
+ * - Input: JSON via stdin (fields: timestamp, cwd)
17
+ * - Output: JSON via stdout
18
+ * - Only preToolUse can control flow via
19
+ * `{"permissionDecision": "deny|allow|ask", "permissionDecisionReason": "..."}`
20
+ * - ALL OTHER hook outputs are IGNORED by the Copilot CLI
21
+ *
22
+ * Orchestration model: in-turn
23
+ * GitHub Copilot CLI does NOT have a stop-hook that can block/restart the
24
+ * agent. The orchestration loop is driven by the agent itself within a
25
+ * single session (in-turn model), not by hook-driven re-entry.
26
+ *
27
+ * CRITICAL: isActive() detection — the official docs do NOT confirm any
28
+ * specific env vars (COPILOT_HOME, COPILOT_GITHUB_TOKEN, etc.) being
29
+ * injected into hook scripts. Hooks receive stdin JSON with `timestamp`
30
+ * and `cwd`. Custom env vars can be set via the `env` field in hooks.json.
31
+ * We use COPILOT_HOME / COPILOT_GITHUB_TOKEN as best-effort discriminators
32
+ * when the CLI is running, but this may need refinement.
33
+ */
34
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
35
+ if (k2 === undefined) k2 = k;
36
+ var desc = Object.getOwnPropertyDescriptor(m, k);
37
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
38
+ desc = { enumerable: true, get: function() { return m[k]; } };
39
+ }
40
+ Object.defineProperty(o, k2, desc);
41
+ }) : (function(o, m, k, k2) {
42
+ if (k2 === undefined) k2 = k;
43
+ o[k2] = m[k];
44
+ }));
45
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
46
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
47
+ }) : function(o, v) {
48
+ o["default"] = v;
49
+ });
50
+ var __importStar = (this && this.__importStar) || (function () {
51
+ var ownKeys = function(o) {
52
+ ownKeys = Object.getOwnPropertyNames || function (o) {
53
+ var ar = [];
54
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
55
+ return ar;
56
+ };
57
+ return ownKeys(o);
58
+ };
59
+ return function (mod) {
60
+ if (mod && mod.__esModule) return mod;
61
+ var result = {};
62
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
63
+ __setModuleDefault(result, mod);
64
+ return result;
65
+ };
66
+ })();
67
+ Object.defineProperty(exports, "__esModule", { value: true });
68
+ exports.createGithubCopilotAdapter = createGithubCopilotAdapter;
69
+ const path = __importStar(require("node:path"));
70
+ const node_fs_1 = require("node:fs");
71
+ const journal_1 = require("../storage/journal");
72
+ const session_1 = require("../session");
73
+ const types_1 = require("./types");
74
+ const context_1 = require("../prompts/context");
75
+ const installSupport_1 = require("./installSupport");
76
+ // ---------------------------------------------------------------------------
77
+ // Constants
78
+ // ---------------------------------------------------------------------------
79
+ const HARNESS_NAME = "github-copilot";
80
+ /** Default state directory for GitHub Copilot CLI sessions */
81
+ const DEFAULT_STATE_DIR = ".a5c/state";
82
+ function createHookLogger(hookName) {
83
+ const logDir = process.env.BABYSITTER_LOG_DIR || ".a5c/logs";
84
+ const logFile = logDir ? path.join(logDir, `${hookName}.log`) : null;
85
+ const context = {};
86
+ if (logFile) {
87
+ try {
88
+ (0, node_fs_1.mkdirSync)(logDir, { recursive: true });
89
+ }
90
+ catch {
91
+ // Best-effort
92
+ }
93
+ }
94
+ function write(level, message) {
95
+ if (!logFile)
96
+ return;
97
+ const ts = new Date().toISOString();
98
+ const ctxParts = Object.entries(context).map(([k, v]) => `${k}=${v}`);
99
+ const ctxStr = ctxParts.length > 0 ? ` [${ctxParts.join(" ")}]` : "";
100
+ const line = `[${level}] ${ts}${ctxStr} ${message}\n`;
101
+ try {
102
+ (0, node_fs_1.appendFileSync)(logFile, line);
103
+ }
104
+ catch {
105
+ // Best-effort
106
+ }
107
+ }
108
+ return {
109
+ info: (msg) => write("INFO", msg),
110
+ warn: (msg) => write("WARN", msg),
111
+ error: (msg) => write("ERROR", msg),
112
+ setContext: (key, value) => {
113
+ context[key] = value;
114
+ },
115
+ };
116
+ }
117
+ // ---------------------------------------------------------------------------
118
+ // Journal event helper
119
+ // ---------------------------------------------------------------------------
120
+ async function appendSessionEndEvent(runDir, data) {
121
+ try {
122
+ await (0, journal_1.appendEvent)({
123
+ runDir,
124
+ eventType: "SESSION_END_HOOK_INVOKED",
125
+ event: {
126
+ ...data,
127
+ harness: HARNESS_NAME,
128
+ timestamp: new Date().toISOString(),
129
+ },
130
+ });
131
+ }
132
+ catch {
133
+ // Best-effort: don't fail the hook if journal write fails
134
+ }
135
+ }
136
+ // ---------------------------------------------------------------------------
137
+ // Stdin reader
138
+ // ---------------------------------------------------------------------------
139
+ function readStdin() {
140
+ return new Promise((resolve, reject) => {
141
+ let data = "";
142
+ process.stdin.setEncoding("utf8");
143
+ process.stdin.on("data", (chunk) => {
144
+ data += chunk;
145
+ });
146
+ process.stdin.on("end", () => resolve(data));
147
+ process.stdin.on("error", reject);
148
+ });
149
+ }
150
+ function parseHookInput(raw) {
151
+ const trimmed = raw.trim();
152
+ if (!trimmed)
153
+ return {};
154
+ try {
155
+ const parsed = JSON.parse(trimmed);
156
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
157
+ return parsed;
158
+ }
159
+ }
160
+ catch {
161
+ // Malformed JSON — treat as empty
162
+ }
163
+ return {};
164
+ }
165
+ function safeStr(obj, key) {
166
+ const val = obj[key];
167
+ return typeof val === "string" ? val : "";
168
+ }
169
+ // ---------------------------------------------------------------------------
170
+ // Cleanup helper
171
+ // ---------------------------------------------------------------------------
172
+ async function cleanupSession(filePath) {
173
+ try {
174
+ await (0, session_1.deleteSessionFile)(filePath);
175
+ }
176
+ catch {
177
+ // Best-effort cleanup
178
+ }
179
+ }
180
+ // ---------------------------------------------------------------------------
181
+ // State directory resolution
182
+ // ---------------------------------------------------------------------------
183
+ function resolveStateDirInternal(args) {
184
+ if (args.stateDir)
185
+ return path.resolve(args.stateDir);
186
+ if (process.env.BABYSITTER_STATE_DIR) {
187
+ return path.resolve(process.env.BABYSITTER_STATE_DIR);
188
+ }
189
+ if (args.pluginRoot) {
190
+ return path.resolve(args.pluginRoot, "skills", "babysit", "state");
191
+ }
192
+ // Check Copilot plugin root env vars
193
+ const pluginData = process.env.CLAUDE_PLUGIN_DATA ||
194
+ process.env.COPILOT_PLUGIN_ROOT;
195
+ if (pluginData) {
196
+ return path.resolve(pluginData, "skills", "babysit", "state");
197
+ }
198
+ // Default: project-relative .a5c/state/
199
+ return path.resolve(DEFAULT_STATE_DIR);
200
+ }
201
+ // ---------------------------------------------------------------------------
202
+ // Plugin root resolution
203
+ // ---------------------------------------------------------------------------
204
+ function resolvePluginRootInternal(args) {
205
+ const root = args.pluginRoot ||
206
+ process.env.CLAUDE_PLUGIN_DATA ||
207
+ process.env.COPILOT_PLUGIN_ROOT;
208
+ return root ? path.resolve(root) : undefined;
209
+ }
210
+ // ---------------------------------------------------------------------------
211
+ // Session ID resolution
212
+ // ---------------------------------------------------------------------------
213
+ function resolveSessionIdInternal(parsed) {
214
+ // 1. Explicit arg (highest priority)
215
+ if (parsed.sessionId)
216
+ return parsed.sessionId;
217
+ // 2. Env var (best-effort — official docs do not confirm this is injected)
218
+ if (process.env.COPILOT_SESSION_ID)
219
+ return process.env.COPILOT_SESSION_ID;
220
+ // 3. Env file (if COPILOT_ENV_FILE or CLAUDE_ENV_FILE exists)
221
+ const envFile = process.env.COPILOT_ENV_FILE || process.env.CLAUDE_ENV_FILE;
222
+ if (envFile) {
223
+ try {
224
+ const content = (0, node_fs_1.readFileSync)(envFile, "utf-8");
225
+ const match = content.match(/(?:^|\n)\s*(?:export\s+)?COPILOT_SESSION_ID="([^"]+)"/);
226
+ return match?.[1] || undefined;
227
+ }
228
+ catch {
229
+ // Fall through
230
+ }
231
+ }
232
+ // 4. Session ID from hook stdin JSON is resolved at hook handler time, not here.
233
+ // Copilot CLI hooks receive stdin JSON with `timestamp` and `cwd` fields;
234
+ // session_id may be available if custom hook config passes it.
235
+ return undefined;
236
+ }
237
+ // ---------------------------------------------------------------------------
238
+ // Session-end hook handler (cleanup only)
239
+ // ---------------------------------------------------------------------------
240
+ /**
241
+ * Handles the sessionEnd hook event for GitHub Copilot CLI.
242
+ *
243
+ * IMPORTANT: Copilot CLI ignores all output from sessionEnd hooks — only
244
+ * preToolUse can control agent flow via permissionDecision. This handler
245
+ * performs best-effort cleanup (session state files, journal events) and
246
+ * always returns exit code 0.
247
+ *
248
+ * Since there is no stop-hook mechanism, orchestration loop control is
249
+ * handled in-turn by the agent itself, not by this hook.
250
+ */
251
+ async function handleSessionEndHookImpl(args) {
252
+ const { verbose } = args;
253
+ const log = createHookLogger("babysitter-copilot-session-end-hook");
254
+ log.info("handleSessionEndHook started (github-copilot)");
255
+ // 1. Read hook input JSON from stdin
256
+ let rawInput;
257
+ try {
258
+ rawInput = await readStdin();
259
+ }
260
+ catch (e) {
261
+ const msg = e instanceof Error ? e.message : String(e);
262
+ log.warn(`stdin read error: ${msg}`);
263
+ process.stdout.write("{}\n");
264
+ return 0;
265
+ }
266
+ finally {
267
+ if (typeof process.stdin.unref === "function") {
268
+ process.stdin.unref();
269
+ }
270
+ }
271
+ const hookInput = parseHookInput(rawInput);
272
+ log.info("Hook input received");
273
+ // 2. Resolve session ID from hook input (stdin JSON) or env var fallback
274
+ // Note: official docs do not confirm env var injection, but we check as fallback
275
+ const sessionId = safeStr(hookInput, "session_id") ||
276
+ process.env.COPILOT_SESSION_ID ||
277
+ "";
278
+ if (!sessionId) {
279
+ log.info("No session ID in hook input — allowing exit");
280
+ process.stdout.write("{}\n");
281
+ return 0;
282
+ }
283
+ log.setContext("session", sessionId);
284
+ log.info(`Session ID: ${sessionId}`);
285
+ // 3. Resolve state directory
286
+ const stateDir = resolveStateDirInternal(args);
287
+ const runsDir = args.runsDir || ".a5c/runs";
288
+ log.info(`Resolved stateDir: ${stateDir}`);
289
+ // 4. Read session state file
290
+ let filePath = (0, session_1.getSessionFilePath)(stateDir, sessionId);
291
+ log.info(`Checking session file at: ${filePath}`);
292
+ let sessionFile;
293
+ try {
294
+ if (!(await (0, session_1.sessionFileExists)(filePath))) {
295
+ // Fallback: check .a5c/state/ directory
296
+ const fallbackPath = (0, session_1.getSessionFilePath)(path.resolve(DEFAULT_STATE_DIR), sessionId);
297
+ log.info(`Primary state file not found, trying fallback: ${fallbackPath}`);
298
+ if (await (0, session_1.sessionFileExists)(fallbackPath)) {
299
+ filePath = fallbackPath;
300
+ log.info(`Found session file at fallback path: ${filePath}`);
301
+ }
302
+ else {
303
+ log.info(`No active babysitter loop for session ${sessionId} — allowing exit`);
304
+ process.stdout.write("{}\n");
305
+ return 0;
306
+ }
307
+ }
308
+ sessionFile = await (0, session_1.readSessionFile)(filePath);
309
+ }
310
+ catch {
311
+ log.warn(`Session file read error at ${filePath} — allowing exit`);
312
+ process.stdout.write("{}\n");
313
+ return 0;
314
+ }
315
+ const { state } = sessionFile;
316
+ const iteration = state.iteration;
317
+ const runId = state.runId ?? "";
318
+ if (runId)
319
+ log.setContext("run", runId);
320
+ // 5. If no run is bound, just clean up session state
321
+ if (!runId) {
322
+ log.info("No run associated with session — cleaning up");
323
+ await cleanupSession(filePath);
324
+ process.stdout.write("{}\n");
325
+ return 0;
326
+ }
327
+ // 6. Log session-end event to the run journal (best-effort)
328
+ try {
329
+ const runDir = path.isAbsolute(runId)
330
+ ? runId
331
+ : path.join(runsDir, runId);
332
+ await appendSessionEndEvent(runDir, {
333
+ sessionId,
334
+ iteration: state.iteration,
335
+ reason: "session_ended",
336
+ });
337
+ }
338
+ catch {
339
+ // Best-effort: don't fail the hook if journal write fails
340
+ }
341
+ // 7. Clean up session state file
342
+ // Note: Copilot CLI ignores sessionEnd hook output — no flow control
343
+ // is possible here. Orchestration loop is driven in-turn by the agent.
344
+ await cleanupSession(filePath);
345
+ log.info(`Session ended, cleanup complete (iteration=${iteration})`);
346
+ if (verbose) {
347
+ process.stderr.write(`[hook:session-end] Session ${sessionId} ended, cleanup complete\n`);
348
+ }
349
+ // Output is ignored by Copilot CLI but we write valid JSON for consistency
350
+ process.stdout.write("{}\n");
351
+ return 0;
352
+ }
353
+ // ---------------------------------------------------------------------------
354
+ // SessionStart hook handler
355
+ // ---------------------------------------------------------------------------
356
+ async function handleSessionStartHookImpl(args) {
357
+ const { verbose } = args;
358
+ const log = createHookLogger("babysitter-copilot-session-start-hook");
359
+ log.info("handleSessionStartHook started (github-copilot)");
360
+ // 1. Read hook input JSON from stdin
361
+ let rawInput;
362
+ try {
363
+ rawInput = await readStdin();
364
+ }
365
+ catch {
366
+ process.stdout.write("{}\n");
367
+ return 0;
368
+ }
369
+ finally {
370
+ if (typeof process.stdin.unref === "function") {
371
+ process.stdin.unref();
372
+ }
373
+ }
374
+ const hookInput = parseHookInput(rawInput);
375
+ // 2. Resolve session ID
376
+ const sessionId = safeStr(hookInput, "session_id") ||
377
+ process.env.COPILOT_SESSION_ID ||
378
+ "";
379
+ if (!sessionId) {
380
+ log.info("No session ID in hook input — skipping state file creation");
381
+ process.stdout.write("{}\n");
382
+ return 0;
383
+ }
384
+ log.setContext("session", sessionId);
385
+ log.info(`Session ID: ${sessionId}`);
386
+ // 3. Write session ID to env file if available
387
+ const envFile = process.env.COPILOT_ENV_FILE || process.env.CLAUDE_ENV_FILE;
388
+ if (envFile) {
389
+ try {
390
+ (0, node_fs_1.appendFileSync)(envFile, `export COPILOT_SESSION_ID="${sessionId}"\n`);
391
+ }
392
+ catch {
393
+ if (verbose) {
394
+ process.stderr.write(`[hook:run session-start] Failed to write to env file: ${envFile}\n`);
395
+ }
396
+ }
397
+ }
398
+ // 4. Resolve state directory and create baseline session file
399
+ const stateDir = resolveStateDirInternal(args);
400
+ log.info(`Resolved stateDir: ${stateDir}`);
401
+ const filePath = (0, session_1.getSessionFilePath)(stateDir, sessionId);
402
+ try {
403
+ if (!(await (0, session_1.sessionFileExists)(filePath))) {
404
+ const nowTs = (0, session_1.getCurrentTimestamp)();
405
+ const state = {
406
+ active: true,
407
+ iteration: 1,
408
+ maxIterations: 256,
409
+ runId: "",
410
+ startedAt: nowTs,
411
+ lastIterationAt: nowTs,
412
+ iterationTimes: [],
413
+ };
414
+ await (0, session_1.writeSessionFile)(filePath, state, "");
415
+ log.info(`Created session state: ${filePath}`);
416
+ if (verbose) {
417
+ process.stderr.write(`[hook:run session-start] Created session state: ${filePath}\n`);
418
+ }
419
+ }
420
+ else {
421
+ log.info(`Session state already exists: ${filePath}`);
422
+ }
423
+ }
424
+ catch (e) {
425
+ const msg = e instanceof Error ? e.message : String(e);
426
+ log.warn(`Failed to create session state: ${msg}`);
427
+ if (verbose) {
428
+ process.stderr.write(`[hook:run session-start] Failed to create session state: ${msg}\n`);
429
+ }
430
+ }
431
+ // 5. Output empty object
432
+ process.stdout.write("{}\n");
433
+ return 0;
434
+ }
435
+ // ---------------------------------------------------------------------------
436
+ // Session binding (run:create flow)
437
+ // ---------------------------------------------------------------------------
438
+ async function bindSessionImpl(opts) {
439
+ const { sessionId, runId, maxIterations = 256, prompt, verbose } = opts;
440
+ // Resolve state directory
441
+ const stateDir = resolveStateDirInternal({
442
+ stateDir: opts.stateDir,
443
+ pluginRoot: opts.pluginRoot,
444
+ });
445
+ const filePath = (0, session_1.getSessionFilePath)(stateDir, sessionId);
446
+ // Check for existing session (prevent re-entrant runs)
447
+ if (await (0, session_1.sessionFileExists)(filePath)) {
448
+ try {
449
+ const existing = await (0, session_1.readSessionFile)(filePath);
450
+ if (existing.state.runId && existing.state.runId !== runId) {
451
+ return {
452
+ harness: HARNESS_NAME,
453
+ sessionId,
454
+ stateFile: filePath,
455
+ error: `Session already associated with run: ${existing.state.runId}`,
456
+ };
457
+ }
458
+ // Update existing session with run ID
459
+ await (0, session_1.updateSessionState)(filePath, { runId, active: true }, { state: existing.state, prompt: existing.prompt });
460
+ if (verbose) {
461
+ process.stderr.write(`[run:create] Updated existing session ${sessionId} with run ${runId}\n`);
462
+ }
463
+ return { harness: HARNESS_NAME, sessionId, stateFile: filePath };
464
+ }
465
+ catch {
466
+ // Corrupted state file — overwrite
467
+ }
468
+ }
469
+ // Create new session state with run associated
470
+ const nowTs = (0, session_1.getCurrentTimestamp)();
471
+ const state = {
472
+ active: true,
473
+ iteration: 1,
474
+ maxIterations,
475
+ runId,
476
+ startedAt: nowTs,
477
+ lastIterationAt: nowTs,
478
+ iterationTimes: [],
479
+ };
480
+ try {
481
+ await (0, session_1.writeSessionFile)(filePath, state, prompt);
482
+ }
483
+ catch (e) {
484
+ return {
485
+ harness: HARNESS_NAME,
486
+ sessionId,
487
+ error: `Failed to write session state: ${e instanceof Error ? e.message : String(e)}`,
488
+ };
489
+ }
490
+ if (verbose) {
491
+ process.stderr.write(`[run:create] Session ${sessionId} initialized and bound to run ${runId}\n`);
492
+ }
493
+ return { harness: HARNESS_NAME, sessionId, stateFile: filePath };
494
+ }
495
+ // ---------------------------------------------------------------------------
496
+ // Hook dispatcher path
497
+ // ---------------------------------------------------------------------------
498
+ function findHookDispatcherPathImpl(startCwd) {
499
+ // Copilot CLI hooks are configured via hooks.json (not standalone scripts).
500
+ // Look for hooks.json in standard locations.
501
+ const pluginRoot = resolvePluginRootInternal({});
502
+ if (pluginRoot) {
503
+ const candidate = path.join(path.resolve(pluginRoot), "hooks.json");
504
+ if ((0, node_fs_1.existsSync)(candidate))
505
+ return candidate;
506
+ }
507
+ // Walk up from startCwd looking for hooks.json
508
+ let current = path.resolve(startCwd);
509
+ const root = path.parse(current).root;
510
+ while (current !== root) {
511
+ // Check .github/copilot/hooks.json (standard Copilot location)
512
+ const candidate = path.join(current, ".github", "copilot", "hooks.json");
513
+ if ((0, node_fs_1.existsSync)(candidate))
514
+ return candidate;
515
+ // Check .copilot/hooks.json
516
+ const copilotCandidate = path.join(current, ".copilot", "hooks.json");
517
+ if ((0, node_fs_1.existsSync)(copilotCandidate))
518
+ return copilotCandidate;
519
+ const a5cCandidate = path.join(current, ".a5c", "hooks.json");
520
+ if ((0, node_fs_1.existsSync)(a5cCandidate))
521
+ return a5cCandidate;
522
+ current = path.dirname(current);
523
+ }
524
+ return null;
525
+ }
526
+ // ---------------------------------------------------------------------------
527
+ // Install helpers
528
+ // ---------------------------------------------------------------------------
529
+ async function installCopilotHarness(options) {
530
+ return (0, installSupport_1.installCliViaNpm)({
531
+ harness: HARNESS_NAME,
532
+ cliCommand: "copilot",
533
+ packageName: "@github/copilot",
534
+ summary: "Install the GitHub Copilot CLI globally via npm.",
535
+ options,
536
+ });
537
+ }
538
+ function installCopilotPlugin(_options) {
539
+ // Copilot plugin installation is not yet supported via a published package.
540
+ // Return guidance for manual setup.
541
+ return {
542
+ harness: HARNESS_NAME,
543
+ summary: "GitHub Copilot CLI plugin installation is not yet automated. " +
544
+ "Configure hooks in .github/copilot/ or ~/.copilot/ manually.",
545
+ };
546
+ }
547
+ // ---------------------------------------------------------------------------
548
+ // Adapter factory
549
+ // ---------------------------------------------------------------------------
550
+ function createGithubCopilotAdapter() {
551
+ return {
552
+ name: HARNESS_NAME,
553
+ isActive() {
554
+ // Best-effort detection: official docs do NOT confirm these env vars
555
+ // are injected into hook scripts. However, COPILOT_HOME or
556
+ // COPILOT_GITHUB_TOKEN may be present when running inside a Copilot
557
+ // CLI session. This distinguishes from Claude Code which uses CLAUDE_* vars.
558
+ return !!(process.env.COPILOT_HOME ||
559
+ process.env.COPILOT_GITHUB_TOKEN);
560
+ },
561
+ autoResolvesSessionId() {
562
+ // Session ID comes from hook stdin JSON, not env vars
563
+ return false;
564
+ },
565
+ getMissingSessionIdHint() {
566
+ return ("GitHub Copilot CLI provides session IDs via hook stdin JSON. " +
567
+ "Use --session-id explicitly, or ensure Copilot CLI hooks are configured " +
568
+ "to pass session_id in the stdin payload.");
569
+ },
570
+ supportsHookType(hookType) {
571
+ // Maps to Copilot CLI hook event types (camelCase):
572
+ // session-start → sessionStart
573
+ // session-end → sessionEnd
574
+ // user-prompt-submit → userPromptSubmitted
575
+ // pre-tool-use → preToolUse
576
+ // post-tool-use → postToolUse
577
+ // Note: there is NO stop hook in Copilot CLI.
578
+ const supported = [
579
+ "session-start",
580
+ "session-end",
581
+ "user-prompt-submit",
582
+ "pre-tool-use",
583
+ "post-tool-use",
584
+ ];
585
+ return supported.includes(hookType);
586
+ },
587
+ getCapabilities() {
588
+ // No StopHook — Copilot CLI does not support a stop hook that can
589
+ // block/restart the agent. Orchestration uses the in-turn model.
590
+ return [
591
+ types_1.HarnessCapability.HeadlessPrompt,
592
+ types_1.HarnessCapability.SessionBinding,
593
+ types_1.HarnessCapability.Mcp,
594
+ ];
595
+ },
596
+ resolveSessionId(parsed) {
597
+ return resolveSessionIdInternal(parsed);
598
+ },
599
+ resolveStateDir(args) {
600
+ return resolveStateDirInternal(args);
601
+ },
602
+ resolvePluginRoot(args) {
603
+ return resolvePluginRootInternal(args);
604
+ },
605
+ bindSession(opts) {
606
+ return bindSessionImpl(opts);
607
+ },
608
+ handleStopHook(args) {
609
+ // Copilot CLI has no stop hook — this maps to sessionEnd for cleanup.
610
+ // Output is ignored by the CLI; no flow control is possible.
611
+ return handleSessionEndHookImpl(args);
612
+ },
613
+ handleSessionStartHook(args) {
614
+ return handleSessionStartHookImpl(args);
615
+ },
616
+ findHookDispatcherPath(startCwd) {
617
+ return findHookDispatcherPathImpl(startCwd);
618
+ },
619
+ installHarness(options) {
620
+ return installCopilotHarness(options);
621
+ },
622
+ installPlugin(options) {
623
+ return Promise.resolve(installCopilotPlugin(options));
624
+ },
625
+ getPromptContext(opts) {
626
+ return (0, context_1.createGithubCopilotContext)(opts);
627
+ },
628
+ };
629
+ }
@@ -3,6 +3,7 @@ export { HarnessCapability } from "./types";
3
3
  export { createClaudeCodeAdapter } from "./claudeCode";
4
4
  export { createCodexAdapter } from "./codex";
5
5
  export { createGeminiCliAdapter } from "./geminiCli";
6
+ export { createGithubCopilotAdapter } from "./githubCopilot";
6
7
  export { createPiAdapter } from "./pi";
7
8
  export { createOhMyPiAdapter } from "./ohMyPi";
8
9
  export { createPiSession, PiSessionHandle, type PiEventListener } from "./piWrapper";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/harness/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,cAAc,GACf,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,sBAAsB,EACtB,UAAU,EACV,UAAU,EACV,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEzG,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE7E,OAAO,EACL,4BAA4B,EAC5B,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/harness/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,cAAc,GACf,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,sBAAsB,EACtB,UAAU,EACV,UAAU,EACV,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEzG,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE7E,OAAO,EACL,4BAA4B,EAC5B,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AGENTIC_TOOL_NAMES = exports.createAgenticToolDefinitions = exports.HARNESS_CLI_MAP = exports.buildHarnessArgs = exports.invokeHarness = exports.KNOWN_HARNESSES = exports.checkCliAvailable = exports.detectCallerHarness = exports.discoverHarnesses = exports.resetAdapter = exports.setAdapter = exports.getAdapter = exports.listSupportedHarnesses = exports.getAdapterByName = exports.detectAdapter = exports.createCustomAdapter = exports.createNullAdapter = exports.PiSessionHandle = exports.createPiSession = exports.createOhMyPiAdapter = exports.createPiAdapter = exports.createGeminiCliAdapter = exports.createCodexAdapter = exports.createClaudeCodeAdapter = exports.HarnessCapability = void 0;
3
+ exports.AGENTIC_TOOL_NAMES = exports.createAgenticToolDefinitions = exports.HARNESS_CLI_MAP = exports.buildHarnessArgs = exports.invokeHarness = exports.KNOWN_HARNESSES = exports.checkCliAvailable = exports.detectCallerHarness = exports.discoverHarnesses = exports.resetAdapter = exports.setAdapter = exports.getAdapter = exports.listSupportedHarnesses = exports.getAdapterByName = exports.detectAdapter = exports.createCustomAdapter = exports.createNullAdapter = exports.PiSessionHandle = exports.createPiSession = exports.createOhMyPiAdapter = exports.createPiAdapter = exports.createGithubCopilotAdapter = exports.createGeminiCliAdapter = exports.createCodexAdapter = exports.createClaudeCodeAdapter = exports.HarnessCapability = void 0;
4
4
  var types_1 = require("./types");
5
5
  Object.defineProperty(exports, "HarnessCapability", { enumerable: true, get: function () { return types_1.HarnessCapability; } });
6
6
  var claudeCode_1 = require("./claudeCode");
@@ -9,6 +9,8 @@ var codex_1 = require("./codex");
9
9
  Object.defineProperty(exports, "createCodexAdapter", { enumerable: true, get: function () { return codex_1.createCodexAdapter; } });
10
10
  var geminiCli_1 = require("./geminiCli");
11
11
  Object.defineProperty(exports, "createGeminiCliAdapter", { enumerable: true, get: function () { return geminiCli_1.createGeminiCliAdapter; } });
12
+ var githubCopilot_1 = require("./githubCopilot");
13
+ Object.defineProperty(exports, "createGithubCopilotAdapter", { enumerable: true, get: function () { return githubCopilot_1.createGithubCopilotAdapter; } });
12
14
  var pi_1 = require("./pi");
13
15
  Object.defineProperty(exports, "createPiAdapter", { enumerable: true, get: function () { return pi_1.createPiAdapter; } });
14
16
  var ohMyPi_1 = require("./ohMyPi");
@@ -1 +1 @@
1
- {"version":3,"file":"invoker.d.ts","sourceRoot":"","sources":["../../src/harness/invoker.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAQzE,wDAAwD;AACxD,UAAU,cAAc;IACtB,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kDAAkD;IAClD,aAAa,EAAE,OAAO,CAAC;IACvB,qEAAqE;IACrE,WAAW,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IACpC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAQD;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAc3D,CAAC;AAiDX;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAC5B,MAAM,EAAE,CA+BV;AASD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAmH9B"}
1
+ {"version":3,"file":"invoker.d.ts","sourceRoot":"","sources":["../../src/harness/invoker.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAQzE,wDAAwD;AACxD,UAAU,cAAc;IACtB,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kDAAkD;IAClD,aAAa,EAAE,OAAO,CAAC;IACvB,qEAAqE;IACrE,WAAW,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IACpC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAQD;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAe3D,CAAC;AAiDX;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAC5B,MAAM,EAAE,CA+BV;AASD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAmH9B"}
@@ -64,6 +64,7 @@ exports.HARNESS_CLI_MAP = {
64
64
  pi: { cli: "pi", workspaceFlag: "--workspace", supportsModel: true, promptStyle: "flag" },
65
65
  "oh-my-pi": { cli: "omp", workspaceFlag: "--workspace", supportsModel: true, promptStyle: "flag" },
66
66
  "gemini-cli": { cli: "gemini", supportsModel: true, promptStyle: "flag" },
67
+ "github-copilot": { cli: "copilot", supportsModel: true, promptStyle: "flag" },
67
68
  cursor: { cli: "cursor", supportsModel: false, promptStyle: "flag" },
68
69
  opencode: { cli: "opencode", supportsModel: false, promptStyle: "flag" },
69
70
  };
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/harness/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAyB9C;;;GAGG;AACH,wBAAgB,aAAa,IAAI,cAAc,CAK9C;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAKpE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAEjD;AAQD;;GAEG;AACH,wBAAgB,UAAU,IAAI,cAAc,CAK3C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAExD;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAEnC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/harness/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AA2B9C;;;GAGG;AACH,wBAAgB,aAAa,IAAI,cAAc,CAK9C;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAKpE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAEjD;AAQD;;GAEG;AACH,wBAAgB,UAAU,IAAI,cAAc,CAK3C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAExD;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAEnC"}
@@ -18,6 +18,7 @@ const codex_1 = require("./codex");
18
18
  const geminiCli_1 = require("./geminiCli");
19
19
  const pi_1 = require("./pi");
20
20
  const ohMyPi_1 = require("./ohMyPi");
21
+ const githubCopilot_1 = require("./githubCopilot");
21
22
  const customAdapter_1 = require("./customAdapter");
22
23
  // ---------------------------------------------------------------------------
23
24
  // Registry of known adapters (ordered by priority)
@@ -28,6 +29,7 @@ const knownAdapters = [
28
29
  (0, pi_1.createPiAdapter)(),
29
30
  (0, claudeCode_1.createClaudeCodeAdapter)(),
30
31
  (0, geminiCli_1.createGeminiCliAdapter)(),
32
+ (0, githubCopilot_1.createGithubCopilotAdapter)(),
31
33
  (0, customAdapter_1.createCustomAdapter)(),
32
34
  ];
33
35
  // ---------------------------------------------------------------------------
@@ -12,6 +12,10 @@ export declare function createClaudeCodeContext(overrides?: Partial<PromptContex
12
12
  * Create a PromptContext pre-configured for Codex.
13
13
  */
14
14
  export declare function createCodexContext(overrides?: Partial<PromptContext>): PromptContext;
15
+ /**
16
+ * Create a PromptContext pre-configured for GitHub Copilot CLI.
17
+ */
18
+ export declare function createGithubCopilotContext(overrides?: Partial<PromptContext>): PromptContext;
15
19
  /**
16
20
  * Create a PromptContext pre-configured for PI.
17
21
  */
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/prompts/context.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAUxC;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GACjC,aAAa,CAgCf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GACjC,aAAa,CAiCf;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GACjC,aAAa,CA+Bf"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/prompts/context.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAUxC;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GACjC,aAAa,CAgCf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GACjC,aAAa,CAiCf;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GACjC,aAAa,CAiCf;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GACjC,aAAa,CA+Bf"}
@@ -7,6 +7,7 @@
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.createClaudeCodeContext = createClaudeCodeContext;
9
9
  exports.createCodexContext = createCodexContext;
10
+ exports.createGithubCopilotContext = createGithubCopilotContext;
10
11
  exports.createPiContext = createPiContext;
11
12
  const COMMON_DEFAULTS = {
12
13
  interactive: true,
@@ -85,6 +86,43 @@ function createCodexContext(overrides) {
85
86
  ...overrides,
86
87
  };
87
88
  }
89
+ /**
90
+ * Create a PromptContext pre-configured for GitHub Copilot CLI.
91
+ */
92
+ function createGithubCopilotContext(overrides) {
93
+ return {
94
+ ...COMMON_DEFAULTS,
95
+ harness: 'github-copilot',
96
+ harnessLabel: 'GitHub Copilot CLI',
97
+ capabilities: ['hooks', 'mcp', 'task-tool', 'breakpoint-routing'],
98
+ pluginRootVar: '${COPILOT_PLUGIN_ROOT}',
99
+ // In-turn model: agent drives orchestration loop within a single session.
100
+ // No stop-hook available — Copilot CLI sessionEnd output is ignored.
101
+ loopControlTerm: 'in-turn',
102
+ sessionBindingFlags: '--state-dir .a5c/state',
103
+ hookDriven: false,
104
+ interactiveToolName: 'AskUserQuestion tool',
105
+ sessionEnvVars: 'session_id (via hook stdin JSON, if configured)',
106
+ resumeFlags: '--state-dir .a5c/state',
107
+ cliSetupSnippet: [
108
+ 'Use the installed CLI alias:',
109
+ '',
110
+ '```bash',
111
+ 'CLI="babysitter"',
112
+ '```',
113
+ '',
114
+ 'If it is not available on the path, use:',
115
+ '',
116
+ '```bash',
117
+ 'CLI="npx -y @a5c-ai/babysitter-sdk"',
118
+ '```',
119
+ ].join('\n'),
120
+ iterateFlags: '',
121
+ hasIntentFidelityChecks: false,
122
+ hasNonNegotiables: false,
123
+ ...overrides,
124
+ };
125
+ }
88
126
  /**
89
127
  * Create a PromptContext pre-configured for PI.
90
128
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a5c-ai/babysitter-sdk",
3
- "version": "0.0.183-staging.08ae538f",
3
+ "version": "0.0.183-staging.0a3fc67d",
4
4
  "description": "Storage and run-registry primitives for event-sourced babysitter workflows.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "commonjs",