@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.
- package/dist/harness/discovery.d.ts.map +1 -1
- package/dist/harness/discovery.js +10 -0
- package/dist/harness/githubCopilot.d.ts +35 -0
- package/dist/harness/githubCopilot.d.ts.map +1 -0
- package/dist/harness/githubCopilot.js +629 -0
- package/dist/harness/index.d.ts +1 -0
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +3 -1
- package/dist/harness/invoker.d.ts.map +1 -1
- package/dist/harness/invoker.js +1 -0
- package/dist/harness/registry.d.ts.map +1 -1
- package/dist/harness/registry.js +2 -0
- package/dist/prompts/context.d.ts +4 -0
- package/dist/prompts/context.d.ts.map +1 -1
- package/dist/prompts/context.js +38 -0
- package/package.json +1 -1
|
@@ -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,
|
|
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
|
+
}
|
package/dist/harness/index.d.ts
CHANGED
|
@@ -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"}
|
package/dist/harness/index.js
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/harness/invoker.js
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/harness/registry.js
CHANGED
|
@@ -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"}
|
package/dist/prompts/context.js
CHANGED
|
@@ -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