@willh/subtitle-correction-agent 0.1.1 → 0.1.3
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/index.js +542 -85
- package/package.json +27 -27
package/dist/index.js
CHANGED
|
@@ -3272,21 +3272,81 @@ var require_main = __commonJS((exports) => {
|
|
|
3272
3272
|
// src/index.ts
|
|
3273
3273
|
import * as path6 from "path";
|
|
3274
3274
|
import * as fs5 from "fs";
|
|
3275
|
-
import { fileURLToPath } from "url";
|
|
3275
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3276
3276
|
import { execSync } from "child_process";
|
|
3277
3277
|
|
|
3278
3278
|
// node_modules/@github/copilot-sdk/dist/client.js
|
|
3279
|
-
var
|
|
3279
|
+
var import_node2 = __toESM(require_main(), 1);
|
|
3280
3280
|
import { spawn } from "node:child_process";
|
|
3281
|
+
import { existsSync } from "node:fs";
|
|
3281
3282
|
import { Socket } from "node:net";
|
|
3283
|
+
import { dirname, join } from "node:path";
|
|
3284
|
+
import { fileURLToPath } from "node:url";
|
|
3285
|
+
|
|
3286
|
+
// node_modules/@github/copilot-sdk/dist/generated/rpc.js
|
|
3287
|
+
function createServerRpc(connection) {
|
|
3288
|
+
return {
|
|
3289
|
+
ping: async (params) => connection.sendRequest("ping", params),
|
|
3290
|
+
models: {
|
|
3291
|
+
list: async () => connection.sendRequest("models.list", {})
|
|
3292
|
+
},
|
|
3293
|
+
tools: {
|
|
3294
|
+
list: async (params) => connection.sendRequest("tools.list", params)
|
|
3295
|
+
},
|
|
3296
|
+
account: {
|
|
3297
|
+
getQuota: async () => connection.sendRequest("account.getQuota", {})
|
|
3298
|
+
}
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3301
|
+
function createSessionRpc(connection, sessionId) {
|
|
3302
|
+
return {
|
|
3303
|
+
model: {
|
|
3304
|
+
getCurrent: async () => connection.sendRequest("session.model.getCurrent", { sessionId }),
|
|
3305
|
+
switchTo: async (params) => connection.sendRequest("session.model.switchTo", { sessionId, ...params })
|
|
3306
|
+
},
|
|
3307
|
+
mode: {
|
|
3308
|
+
get: async () => connection.sendRequest("session.mode.get", { sessionId }),
|
|
3309
|
+
set: async (params) => connection.sendRequest("session.mode.set", { sessionId, ...params })
|
|
3310
|
+
},
|
|
3311
|
+
plan: {
|
|
3312
|
+
read: async () => connection.sendRequest("session.plan.read", { sessionId }),
|
|
3313
|
+
update: async (params) => connection.sendRequest("session.plan.update", { sessionId, ...params }),
|
|
3314
|
+
delete: async () => connection.sendRequest("session.plan.delete", { sessionId })
|
|
3315
|
+
},
|
|
3316
|
+
workspace: {
|
|
3317
|
+
listFiles: async () => connection.sendRequest("session.workspace.listFiles", { sessionId }),
|
|
3318
|
+
readFile: async (params) => connection.sendRequest("session.workspace.readFile", { sessionId, ...params }),
|
|
3319
|
+
createFile: async (params) => connection.sendRequest("session.workspace.createFile", { sessionId, ...params })
|
|
3320
|
+
},
|
|
3321
|
+
fleet: {
|
|
3322
|
+
start: async (params) => connection.sendRequest("session.fleet.start", { sessionId, ...params })
|
|
3323
|
+
},
|
|
3324
|
+
agent: {
|
|
3325
|
+
list: async () => connection.sendRequest("session.agent.list", { sessionId }),
|
|
3326
|
+
getCurrent: async () => connection.sendRequest("session.agent.getCurrent", { sessionId }),
|
|
3327
|
+
select: async (params) => connection.sendRequest("session.agent.select", { sessionId, ...params }),
|
|
3328
|
+
deselect: async () => connection.sendRequest("session.agent.deselect", { sessionId })
|
|
3329
|
+
},
|
|
3330
|
+
compaction: {
|
|
3331
|
+
compact: async () => connection.sendRequest("session.compaction.compact", { sessionId })
|
|
3332
|
+
},
|
|
3333
|
+
tools: {
|
|
3334
|
+
handlePendingToolCall: async (params) => connection.sendRequest("session.tools.handlePendingToolCall", { sessionId, ...params })
|
|
3335
|
+
},
|
|
3336
|
+
permissions: {
|
|
3337
|
+
handlePendingPermissionRequest: async (params) => connection.sendRequest("session.permissions.handlePendingPermissionRequest", { sessionId, ...params })
|
|
3338
|
+
}
|
|
3339
|
+
};
|
|
3340
|
+
}
|
|
3282
3341
|
|
|
3283
3342
|
// node_modules/@github/copilot-sdk/dist/sdkProtocolVersion.js
|
|
3284
|
-
var SDK_PROTOCOL_VERSION =
|
|
3343
|
+
var SDK_PROTOCOL_VERSION = 3;
|
|
3285
3344
|
function getSdkProtocolVersion() {
|
|
3286
3345
|
return SDK_PROTOCOL_VERSION;
|
|
3287
3346
|
}
|
|
3288
3347
|
|
|
3289
3348
|
// node_modules/@github/copilot-sdk/dist/session.js
|
|
3349
|
+
var import_node = __toESM(require_main(), 1);
|
|
3290
3350
|
class CopilotSession {
|
|
3291
3351
|
constructor(sessionId, connection, _workspacePath) {
|
|
3292
3352
|
this.sessionId = sessionId;
|
|
@@ -3294,8 +3354,18 @@ class CopilotSession {
|
|
|
3294
3354
|
this._workspacePath = _workspacePath;
|
|
3295
3355
|
}
|
|
3296
3356
|
eventHandlers = /* @__PURE__ */ new Set;
|
|
3357
|
+
typedEventHandlers = /* @__PURE__ */ new Map;
|
|
3297
3358
|
toolHandlers = /* @__PURE__ */ new Map;
|
|
3298
3359
|
permissionHandler;
|
|
3360
|
+
userInputHandler;
|
|
3361
|
+
hooks;
|
|
3362
|
+
_rpc = null;
|
|
3363
|
+
get rpc() {
|
|
3364
|
+
if (!this._rpc) {
|
|
3365
|
+
this._rpc = createSessionRpc(this.connection, this.sessionId);
|
|
3366
|
+
}
|
|
3367
|
+
return this._rpc;
|
|
3368
|
+
}
|
|
3299
3369
|
get workspacePath() {
|
|
3300
3370
|
return this._workspacePath;
|
|
3301
3371
|
}
|
|
@@ -3328,30 +3398,123 @@ class CopilotSession {
|
|
|
3328
3398
|
rejectWithError(error);
|
|
3329
3399
|
}
|
|
3330
3400
|
});
|
|
3401
|
+
let timeoutId;
|
|
3331
3402
|
try {
|
|
3332
3403
|
await this.send(options);
|
|
3333
3404
|
const timeoutPromise = new Promise((_, reject) => {
|
|
3334
|
-
setTimeout(() => reject(new Error(`Timeout after ${effectiveTimeout}ms waiting for session.idle`)), effectiveTimeout);
|
|
3405
|
+
timeoutId = setTimeout(() => reject(new Error(`Timeout after ${effectiveTimeout}ms waiting for session.idle`)), effectiveTimeout);
|
|
3335
3406
|
});
|
|
3336
3407
|
await Promise.race([idlePromise, timeoutPromise]);
|
|
3337
3408
|
return lastAssistantMessage;
|
|
3338
3409
|
} finally {
|
|
3410
|
+
if (timeoutId !== undefined) {
|
|
3411
|
+
clearTimeout(timeoutId);
|
|
3412
|
+
}
|
|
3339
3413
|
unsubscribe();
|
|
3340
3414
|
}
|
|
3341
3415
|
}
|
|
3342
|
-
on(handler) {
|
|
3343
|
-
|
|
3416
|
+
on(eventTypeOrHandler, handler) {
|
|
3417
|
+
if (typeof eventTypeOrHandler === "string" && handler) {
|
|
3418
|
+
const eventType = eventTypeOrHandler;
|
|
3419
|
+
if (!this.typedEventHandlers.has(eventType)) {
|
|
3420
|
+
this.typedEventHandlers.set(eventType, /* @__PURE__ */ new Set);
|
|
3421
|
+
}
|
|
3422
|
+
const storedHandler = handler;
|
|
3423
|
+
this.typedEventHandlers.get(eventType).add(storedHandler);
|
|
3424
|
+
return () => {
|
|
3425
|
+
const handlers = this.typedEventHandlers.get(eventType);
|
|
3426
|
+
if (handlers) {
|
|
3427
|
+
handlers.delete(storedHandler);
|
|
3428
|
+
}
|
|
3429
|
+
};
|
|
3430
|
+
}
|
|
3431
|
+
const wildcardHandler = eventTypeOrHandler;
|
|
3432
|
+
this.eventHandlers.add(wildcardHandler);
|
|
3344
3433
|
return () => {
|
|
3345
|
-
this.eventHandlers.delete(
|
|
3434
|
+
this.eventHandlers.delete(wildcardHandler);
|
|
3346
3435
|
};
|
|
3347
3436
|
}
|
|
3348
3437
|
_dispatchEvent(event) {
|
|
3438
|
+
this._handleBroadcastEvent(event);
|
|
3439
|
+
const typedHandlers = this.typedEventHandlers.get(event.type);
|
|
3440
|
+
if (typedHandlers) {
|
|
3441
|
+
for (const handler of typedHandlers) {
|
|
3442
|
+
try {
|
|
3443
|
+
handler(event);
|
|
3444
|
+
} catch (_error) {}
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3349
3447
|
for (const handler of this.eventHandlers) {
|
|
3350
3448
|
try {
|
|
3351
3449
|
handler(event);
|
|
3352
3450
|
} catch (_error) {}
|
|
3353
3451
|
}
|
|
3354
3452
|
}
|
|
3453
|
+
_handleBroadcastEvent(event) {
|
|
3454
|
+
if (event.type === "external_tool.requested") {
|
|
3455
|
+
const { requestId, toolName } = event.data;
|
|
3456
|
+
const args = event.data.arguments;
|
|
3457
|
+
const toolCallId = event.data.toolCallId;
|
|
3458
|
+
const handler = this.toolHandlers.get(toolName);
|
|
3459
|
+
if (handler) {
|
|
3460
|
+
this._executeToolAndRespond(requestId, toolName, toolCallId, args, handler);
|
|
3461
|
+
}
|
|
3462
|
+
} else if (event.type === "permission.requested") {
|
|
3463
|
+
const { requestId, permissionRequest } = event.data;
|
|
3464
|
+
if (this.permissionHandler) {
|
|
3465
|
+
this._executePermissionAndRespond(requestId, permissionRequest);
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3469
|
+
async _executeToolAndRespond(requestId, toolName, toolCallId, args, handler) {
|
|
3470
|
+
try {
|
|
3471
|
+
const rawResult = await handler(args, {
|
|
3472
|
+
sessionId: this.sessionId,
|
|
3473
|
+
toolCallId,
|
|
3474
|
+
toolName,
|
|
3475
|
+
arguments: args
|
|
3476
|
+
});
|
|
3477
|
+
let result;
|
|
3478
|
+
if (rawResult == null) {
|
|
3479
|
+
result = "";
|
|
3480
|
+
} else if (typeof rawResult === "string") {
|
|
3481
|
+
result = rawResult;
|
|
3482
|
+
} else {
|
|
3483
|
+
result = JSON.stringify(rawResult);
|
|
3484
|
+
}
|
|
3485
|
+
await this.rpc.tools.handlePendingToolCall({ requestId, result });
|
|
3486
|
+
} catch (error) {
|
|
3487
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3488
|
+
try {
|
|
3489
|
+
await this.rpc.tools.handlePendingToolCall({ requestId, error: message });
|
|
3490
|
+
} catch (rpcError) {
|
|
3491
|
+
if (!(rpcError instanceof import_node.ConnectionError || rpcError instanceof import_node.ResponseError)) {
|
|
3492
|
+
throw rpcError;
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
async _executePermissionAndRespond(requestId, permissionRequest) {
|
|
3498
|
+
try {
|
|
3499
|
+
const result = await this.permissionHandler(permissionRequest, {
|
|
3500
|
+
sessionId: this.sessionId
|
|
3501
|
+
});
|
|
3502
|
+
await this.rpc.permissions.handlePendingPermissionRequest({ requestId, result });
|
|
3503
|
+
} catch (_error) {
|
|
3504
|
+
try {
|
|
3505
|
+
await this.rpc.permissions.handlePendingPermissionRequest({
|
|
3506
|
+
requestId,
|
|
3507
|
+
result: {
|
|
3508
|
+
kind: "denied-no-approval-rule-and-could-not-request-from-user"
|
|
3509
|
+
}
|
|
3510
|
+
});
|
|
3511
|
+
} catch (rpcError) {
|
|
3512
|
+
if (!(rpcError instanceof import_node.ConnectionError || rpcError instanceof import_node.ResponseError)) {
|
|
3513
|
+
throw rpcError;
|
|
3514
|
+
}
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3355
3518
|
registerTools(tools) {
|
|
3356
3519
|
this.toolHandlers.clear();
|
|
3357
3520
|
if (!tools) {
|
|
@@ -3367,7 +3530,13 @@ class CopilotSession {
|
|
|
3367
3530
|
registerPermissionHandler(handler) {
|
|
3368
3531
|
this.permissionHandler = handler;
|
|
3369
3532
|
}
|
|
3370
|
-
|
|
3533
|
+
registerUserInputHandler(handler) {
|
|
3534
|
+
this.userInputHandler = handler;
|
|
3535
|
+
}
|
|
3536
|
+
registerHooks(hooks) {
|
|
3537
|
+
this.hooks = hooks;
|
|
3538
|
+
}
|
|
3539
|
+
async _handlePermissionRequestV2(request) {
|
|
3371
3540
|
if (!this.permissionHandler) {
|
|
3372
3541
|
return { kind: "denied-no-approval-rule-and-could-not-request-from-user" };
|
|
3373
3542
|
}
|
|
@@ -3380,28 +3549,75 @@ class CopilotSession {
|
|
|
3380
3549
|
return { kind: "denied-no-approval-rule-and-could-not-request-from-user" };
|
|
3381
3550
|
}
|
|
3382
3551
|
}
|
|
3552
|
+
async _handleUserInputRequest(request) {
|
|
3553
|
+
if (!this.userInputHandler) {
|
|
3554
|
+
throw new Error("User input requested but no handler registered");
|
|
3555
|
+
}
|
|
3556
|
+
try {
|
|
3557
|
+
const result = await this.userInputHandler(request, {
|
|
3558
|
+
sessionId: this.sessionId
|
|
3559
|
+
});
|
|
3560
|
+
return result;
|
|
3561
|
+
} catch (error) {
|
|
3562
|
+
throw error;
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
3565
|
+
async _handleHooksInvoke(hookType, input) {
|
|
3566
|
+
if (!this.hooks) {
|
|
3567
|
+
return;
|
|
3568
|
+
}
|
|
3569
|
+
const handlerMap = {
|
|
3570
|
+
preToolUse: this.hooks.onPreToolUse,
|
|
3571
|
+
postToolUse: this.hooks.onPostToolUse,
|
|
3572
|
+
userPromptSubmitted: this.hooks.onUserPromptSubmitted,
|
|
3573
|
+
sessionStart: this.hooks.onSessionStart,
|
|
3574
|
+
sessionEnd: this.hooks.onSessionEnd,
|
|
3575
|
+
errorOccurred: this.hooks.onErrorOccurred
|
|
3576
|
+
};
|
|
3577
|
+
const handler = handlerMap[hookType];
|
|
3578
|
+
if (!handler) {
|
|
3579
|
+
return;
|
|
3580
|
+
}
|
|
3581
|
+
try {
|
|
3582
|
+
const result = await handler(input, { sessionId: this.sessionId });
|
|
3583
|
+
return result;
|
|
3584
|
+
} catch (_error) {
|
|
3585
|
+
return;
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3383
3588
|
async getMessages() {
|
|
3384
3589
|
const response = await this.connection.sendRequest("session.getMessages", {
|
|
3385
3590
|
sessionId: this.sessionId
|
|
3386
3591
|
});
|
|
3387
3592
|
return response.events;
|
|
3388
3593
|
}
|
|
3389
|
-
async
|
|
3594
|
+
async disconnect() {
|
|
3390
3595
|
await this.connection.sendRequest("session.destroy", {
|
|
3391
3596
|
sessionId: this.sessionId
|
|
3392
3597
|
});
|
|
3393
3598
|
this.eventHandlers.clear();
|
|
3599
|
+
this.typedEventHandlers.clear();
|
|
3394
3600
|
this.toolHandlers.clear();
|
|
3395
3601
|
this.permissionHandler = undefined;
|
|
3396
3602
|
}
|
|
3603
|
+
async destroy() {
|
|
3604
|
+
return this.disconnect();
|
|
3605
|
+
}
|
|
3606
|
+
async[Symbol.asyncDispose]() {
|
|
3607
|
+
return this.disconnect();
|
|
3608
|
+
}
|
|
3397
3609
|
async abort() {
|
|
3398
3610
|
await this.connection.sendRequest("session.abort", {
|
|
3399
3611
|
sessionId: this.sessionId
|
|
3400
3612
|
});
|
|
3401
3613
|
}
|
|
3614
|
+
async setModel(model) {
|
|
3615
|
+
await this.rpc.model.switchTo({ modelId: model });
|
|
3616
|
+
}
|
|
3402
3617
|
}
|
|
3403
3618
|
|
|
3404
3619
|
// node_modules/@github/copilot-sdk/dist/client.js
|
|
3620
|
+
var MIN_PROTOCOL_VERSION = 2;
|
|
3405
3621
|
function isZodSchema(value) {
|
|
3406
3622
|
return value != null && typeof value === "object" && "toJSONSchema" in value && typeof value.toJSONSchema === "function";
|
|
3407
3623
|
}
|
|
@@ -3413,6 +3629,17 @@ function toJsonSchema(parameters) {
|
|
|
3413
3629
|
}
|
|
3414
3630
|
return parameters;
|
|
3415
3631
|
}
|
|
3632
|
+
function getNodeExecPath() {
|
|
3633
|
+
if (process.versions.bun) {
|
|
3634
|
+
return "node";
|
|
3635
|
+
}
|
|
3636
|
+
return process.execPath;
|
|
3637
|
+
}
|
|
3638
|
+
function getBundledCliPath() {
|
|
3639
|
+
const sdkUrl = import.meta.resolve("@github/copilot/sdk");
|
|
3640
|
+
const sdkPath = fileURLToPath(sdkUrl);
|
|
3641
|
+
return join(dirname(dirname(sdkPath)), "index.js");
|
|
3642
|
+
}
|
|
3416
3643
|
|
|
3417
3644
|
class CopilotClient {
|
|
3418
3645
|
cliProcess = null;
|
|
@@ -3422,30 +3649,59 @@ class CopilotClient {
|
|
|
3422
3649
|
actualHost = "localhost";
|
|
3423
3650
|
state = "disconnected";
|
|
3424
3651
|
sessions = /* @__PURE__ */ new Map;
|
|
3652
|
+
stderrBuffer = "";
|
|
3425
3653
|
options;
|
|
3426
3654
|
isExternalServer = false;
|
|
3427
3655
|
forceStopping = false;
|
|
3656
|
+
modelsCache = null;
|
|
3657
|
+
modelsCacheLock = Promise.resolve();
|
|
3658
|
+
sessionLifecycleHandlers = /* @__PURE__ */ new Set;
|
|
3659
|
+
typedLifecycleHandlers = /* @__PURE__ */ new Map;
|
|
3660
|
+
_rpc = null;
|
|
3661
|
+
processExitPromise = null;
|
|
3662
|
+
negotiatedProtocolVersion = null;
|
|
3663
|
+
get rpc() {
|
|
3664
|
+
if (!this.connection) {
|
|
3665
|
+
throw new Error("Client is not connected. Call start() first.");
|
|
3666
|
+
}
|
|
3667
|
+
if (!this._rpc) {
|
|
3668
|
+
this._rpc = createServerRpc(this.connection);
|
|
3669
|
+
}
|
|
3670
|
+
return this._rpc;
|
|
3671
|
+
}
|
|
3428
3672
|
constructor(options = {}) {
|
|
3429
3673
|
if (options.cliUrl && (options.useStdio === true || options.cliPath)) {
|
|
3430
3674
|
throw new Error("cliUrl is mutually exclusive with useStdio and cliPath");
|
|
3431
3675
|
}
|
|
3676
|
+
if (options.isChildProcess && (options.cliUrl || options.useStdio === false)) {
|
|
3677
|
+
throw new Error("isChildProcess must be used in conjunction with useStdio and not with cliUrl");
|
|
3678
|
+
}
|
|
3679
|
+
if (options.cliUrl && (options.githubToken || options.useLoggedInUser !== undefined)) {
|
|
3680
|
+
throw new Error("githubToken and useLoggedInUser cannot be used with cliUrl (external server manages its own auth)");
|
|
3681
|
+
}
|
|
3432
3682
|
if (options.cliUrl) {
|
|
3433
3683
|
const { host, port } = this.parseCliUrl(options.cliUrl);
|
|
3434
3684
|
this.actualHost = host;
|
|
3435
3685
|
this.actualPort = port;
|
|
3436
3686
|
this.isExternalServer = true;
|
|
3437
3687
|
}
|
|
3688
|
+
if (options.isChildProcess) {
|
|
3689
|
+
this.isExternalServer = true;
|
|
3690
|
+
}
|
|
3438
3691
|
this.options = {
|
|
3439
|
-
cliPath: options.cliPath ||
|
|
3692
|
+
cliPath: options.cliPath || getBundledCliPath(),
|
|
3440
3693
|
cliArgs: options.cliArgs ?? [],
|
|
3441
3694
|
cwd: options.cwd ?? process.cwd(),
|
|
3442
3695
|
port: options.port || 0,
|
|
3443
3696
|
useStdio: options.cliUrl ? false : options.useStdio ?? true,
|
|
3697
|
+
isChildProcess: options.isChildProcess ?? false,
|
|
3444
3698
|
cliUrl: options.cliUrl,
|
|
3445
3699
|
logLevel: options.logLevel || "debug",
|
|
3446
3700
|
autoStart: options.autoStart ?? true,
|
|
3447
3701
|
autoRestart: options.autoRestart ?? true,
|
|
3448
|
-
env: options.env ?? process.env
|
|
3702
|
+
env: options.env ?? process.env,
|
|
3703
|
+
githubToken: options.githubToken,
|
|
3704
|
+
useLoggedInUser: options.useLoggedInUser ?? (options.githubToken ? false : true)
|
|
3449
3705
|
};
|
|
3450
3706
|
}
|
|
3451
3707
|
parseCliUrl(url) {
|
|
@@ -3488,7 +3744,7 @@ class CopilotClient {
|
|
|
3488
3744
|
let lastError = null;
|
|
3489
3745
|
for (let attempt = 1;attempt <= 3; attempt++) {
|
|
3490
3746
|
try {
|
|
3491
|
-
await session.
|
|
3747
|
+
await session.disconnect();
|
|
3492
3748
|
lastError = null;
|
|
3493
3749
|
break;
|
|
3494
3750
|
} catch (error) {
|
|
@@ -3500,7 +3756,7 @@ class CopilotClient {
|
|
|
3500
3756
|
}
|
|
3501
3757
|
}
|
|
3502
3758
|
if (lastError) {
|
|
3503
|
-
errors.push(new Error(`Failed to
|
|
3759
|
+
errors.push(new Error(`Failed to disconnect session ${sessionId} after 3 attempts: ${lastError.message}`));
|
|
3504
3760
|
}
|
|
3505
3761
|
}
|
|
3506
3762
|
this.sessions.clear();
|
|
@@ -3511,7 +3767,9 @@ class CopilotClient {
|
|
|
3511
3767
|
errors.push(new Error(`Failed to dispose connection: ${error instanceof Error ? error.message : String(error)}`));
|
|
3512
3768
|
}
|
|
3513
3769
|
this.connection = null;
|
|
3770
|
+
this._rpc = null;
|
|
3514
3771
|
}
|
|
3772
|
+
this.modelsCache = null;
|
|
3515
3773
|
if (this.socket) {
|
|
3516
3774
|
try {
|
|
3517
3775
|
this.socket.end();
|
|
@@ -3530,6 +3788,8 @@ class CopilotClient {
|
|
|
3530
3788
|
}
|
|
3531
3789
|
this.state = "disconnected";
|
|
3532
3790
|
this.actualPort = null;
|
|
3791
|
+
this.stderrBuffer = "";
|
|
3792
|
+
this.processExitPromise = null;
|
|
3533
3793
|
return errors;
|
|
3534
3794
|
}
|
|
3535
3795
|
async forceStop() {
|
|
@@ -3540,7 +3800,9 @@ class CopilotClient {
|
|
|
3540
3800
|
this.connection.dispose();
|
|
3541
3801
|
} catch {}
|
|
3542
3802
|
this.connection = null;
|
|
3803
|
+
this._rpc = null;
|
|
3543
3804
|
}
|
|
3805
|
+
this.modelsCache = null;
|
|
3544
3806
|
if (this.socket) {
|
|
3545
3807
|
try {
|
|
3546
3808
|
this.socket.destroy();
|
|
@@ -3555,8 +3817,13 @@ class CopilotClient {
|
|
|
3555
3817
|
}
|
|
3556
3818
|
this.state = "disconnected";
|
|
3557
3819
|
this.actualPort = null;
|
|
3820
|
+
this.stderrBuffer = "";
|
|
3821
|
+
this.processExitPromise = null;
|
|
3558
3822
|
}
|
|
3559
|
-
async createSession(config
|
|
3823
|
+
async createSession(config) {
|
|
3824
|
+
if (!config?.onPermissionRequest) {
|
|
3825
|
+
throw new Error("An onPermissionRequest handler is required when creating a session. For example, to allow all permissions, use { onPermissionRequest: approveAll }.");
|
|
3826
|
+
}
|
|
3560
3827
|
if (!this.connection) {
|
|
3561
3828
|
if (this.options.autoStart) {
|
|
3562
3829
|
await this.start();
|
|
@@ -3567,18 +3834,25 @@ class CopilotClient {
|
|
|
3567
3834
|
const response = await this.connection.sendRequest("session.create", {
|
|
3568
3835
|
model: config.model,
|
|
3569
3836
|
sessionId: config.sessionId,
|
|
3837
|
+
clientName: config.clientName,
|
|
3838
|
+
reasoningEffort: config.reasoningEffort,
|
|
3570
3839
|
tools: config.tools?.map((tool) => ({
|
|
3571
3840
|
name: tool.name,
|
|
3572
3841
|
description: tool.description,
|
|
3573
|
-
parameters: toJsonSchema(tool.parameters)
|
|
3842
|
+
parameters: toJsonSchema(tool.parameters),
|
|
3843
|
+
overridesBuiltInTool: tool.overridesBuiltInTool
|
|
3574
3844
|
})),
|
|
3575
3845
|
systemMessage: config.systemMessage,
|
|
3576
3846
|
availableTools: config.availableTools,
|
|
3577
3847
|
excludedTools: config.excludedTools,
|
|
3578
3848
|
provider: config.provider,
|
|
3579
|
-
requestPermission:
|
|
3849
|
+
requestPermission: true,
|
|
3850
|
+
requestUserInput: !!config.onUserInputRequest,
|
|
3851
|
+
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
3852
|
+
workingDirectory: config.workingDirectory,
|
|
3580
3853
|
streaming: config.streaming,
|
|
3581
3854
|
mcpServers: config.mcpServers,
|
|
3855
|
+
envValueMode: "direct",
|
|
3582
3856
|
customAgents: config.customAgents,
|
|
3583
3857
|
configDir: config.configDir,
|
|
3584
3858
|
skillDirectories: config.skillDirectories,
|
|
@@ -3588,13 +3862,20 @@ class CopilotClient {
|
|
|
3588
3862
|
const { sessionId, workspacePath } = response;
|
|
3589
3863
|
const session = new CopilotSession(sessionId, this.connection, workspacePath);
|
|
3590
3864
|
session.registerTools(config.tools);
|
|
3591
|
-
|
|
3592
|
-
|
|
3865
|
+
session.registerPermissionHandler(config.onPermissionRequest);
|
|
3866
|
+
if (config.onUserInputRequest) {
|
|
3867
|
+
session.registerUserInputHandler(config.onUserInputRequest);
|
|
3868
|
+
}
|
|
3869
|
+
if (config.hooks) {
|
|
3870
|
+
session.registerHooks(config.hooks);
|
|
3593
3871
|
}
|
|
3594
3872
|
this.sessions.set(sessionId, session);
|
|
3595
3873
|
return session;
|
|
3596
3874
|
}
|
|
3597
|
-
async resumeSession(sessionId, config
|
|
3875
|
+
async resumeSession(sessionId, config) {
|
|
3876
|
+
if (!config?.onPermissionRequest) {
|
|
3877
|
+
throw new Error("An onPermissionRequest handler is required when resuming a session. For example, to allow all permissions, use { onPermissionRequest: approveAll }.");
|
|
3878
|
+
}
|
|
3598
3879
|
if (!this.connection) {
|
|
3599
3880
|
if (this.options.autoStart) {
|
|
3600
3881
|
await this.start();
|
|
@@ -3604,24 +3885,42 @@ class CopilotClient {
|
|
|
3604
3885
|
}
|
|
3605
3886
|
const response = await this.connection.sendRequest("session.resume", {
|
|
3606
3887
|
sessionId,
|
|
3888
|
+
clientName: config.clientName,
|
|
3889
|
+
model: config.model,
|
|
3890
|
+
reasoningEffort: config.reasoningEffort,
|
|
3891
|
+
systemMessage: config.systemMessage,
|
|
3892
|
+
availableTools: config.availableTools,
|
|
3893
|
+
excludedTools: config.excludedTools,
|
|
3607
3894
|
tools: config.tools?.map((tool) => ({
|
|
3608
3895
|
name: tool.name,
|
|
3609
3896
|
description: tool.description,
|
|
3610
|
-
parameters: toJsonSchema(tool.parameters)
|
|
3897
|
+
parameters: toJsonSchema(tool.parameters),
|
|
3898
|
+
overridesBuiltInTool: tool.overridesBuiltInTool
|
|
3611
3899
|
})),
|
|
3612
3900
|
provider: config.provider,
|
|
3613
|
-
requestPermission:
|
|
3901
|
+
requestPermission: true,
|
|
3902
|
+
requestUserInput: !!config.onUserInputRequest,
|
|
3903
|
+
hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
|
|
3904
|
+
workingDirectory: config.workingDirectory,
|
|
3905
|
+
configDir: config.configDir,
|
|
3614
3906
|
streaming: config.streaming,
|
|
3615
3907
|
mcpServers: config.mcpServers,
|
|
3908
|
+
envValueMode: "direct",
|
|
3616
3909
|
customAgents: config.customAgents,
|
|
3617
3910
|
skillDirectories: config.skillDirectories,
|
|
3618
|
-
disabledSkills: config.disabledSkills
|
|
3911
|
+
disabledSkills: config.disabledSkills,
|
|
3912
|
+
infiniteSessions: config.infiniteSessions,
|
|
3913
|
+
disableResume: config.disableResume
|
|
3619
3914
|
});
|
|
3620
3915
|
const { sessionId: resumedSessionId, workspacePath } = response;
|
|
3621
3916
|
const session = new CopilotSession(resumedSessionId, this.connection, workspacePath);
|
|
3622
3917
|
session.registerTools(config.tools);
|
|
3623
|
-
|
|
3624
|
-
|
|
3918
|
+
session.registerPermissionHandler(config.onPermissionRequest);
|
|
3919
|
+
if (config.onUserInputRequest) {
|
|
3920
|
+
session.registerUserInputHandler(config.onUserInputRequest);
|
|
3921
|
+
}
|
|
3922
|
+
if (config.hooks) {
|
|
3923
|
+
session.registerHooks(config.hooks);
|
|
3625
3924
|
}
|
|
3626
3925
|
this.sessions.set(resumedSessionId, session);
|
|
3627
3926
|
return session;
|
|
@@ -3654,20 +3953,40 @@ class CopilotClient {
|
|
|
3654
3953
|
if (!this.connection) {
|
|
3655
3954
|
throw new Error("Client not connected");
|
|
3656
3955
|
}
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3956
|
+
await this.modelsCacheLock;
|
|
3957
|
+
let resolveLock;
|
|
3958
|
+
this.modelsCacheLock = new Promise((resolve) => {
|
|
3959
|
+
resolveLock = resolve;
|
|
3960
|
+
});
|
|
3961
|
+
try {
|
|
3962
|
+
if (this.modelsCache !== null) {
|
|
3963
|
+
return [...this.modelsCache];
|
|
3964
|
+
}
|
|
3965
|
+
const result = await this.connection.sendRequest("models.list", {});
|
|
3966
|
+
const response = result;
|
|
3967
|
+
const models = response.models;
|
|
3968
|
+
this.modelsCache = models;
|
|
3969
|
+
return [...models];
|
|
3970
|
+
} finally {
|
|
3971
|
+
resolveLock();
|
|
3972
|
+
}
|
|
3660
3973
|
}
|
|
3661
3974
|
async verifyProtocolVersion() {
|
|
3662
|
-
const
|
|
3663
|
-
|
|
3975
|
+
const maxVersion = getSdkProtocolVersion();
|
|
3976
|
+
let pingResult;
|
|
3977
|
+
if (this.processExitPromise) {
|
|
3978
|
+
pingResult = await Promise.race([this.ping(), this.processExitPromise]);
|
|
3979
|
+
} else {
|
|
3980
|
+
pingResult = await this.ping();
|
|
3981
|
+
}
|
|
3664
3982
|
const serverVersion = pingResult.protocolVersion;
|
|
3665
3983
|
if (serverVersion === undefined) {
|
|
3666
|
-
throw new Error(`SDK protocol version mismatch: SDK
|
|
3984
|
+
throw new Error(`SDK protocol version mismatch: SDK supports versions ${MIN_PROTOCOL_VERSION}-${maxVersion}, but server does not report a protocol version. Please update your server to ensure compatibility.`);
|
|
3667
3985
|
}
|
|
3668
|
-
if (serverVersion
|
|
3669
|
-
throw new Error(`SDK protocol version mismatch: SDK
|
|
3986
|
+
if (serverVersion < MIN_PROTOCOL_VERSION || serverVersion > maxVersion) {
|
|
3987
|
+
throw new Error(`SDK protocol version mismatch: SDK supports versions ${MIN_PROTOCOL_VERSION}-${maxVersion}, but server reports version ${serverVersion}. Please update your SDK or server to ensure compatibility.`);
|
|
3670
3988
|
}
|
|
3989
|
+
this.negotiatedProtocolVersion = serverVersion;
|
|
3671
3990
|
}
|
|
3672
3991
|
async getLastSessionId() {
|
|
3673
3992
|
if (!this.connection) {
|
|
@@ -3689,25 +4008,66 @@ class CopilotClient {
|
|
|
3689
4008
|
}
|
|
3690
4009
|
this.sessions.delete(sessionId);
|
|
3691
4010
|
}
|
|
3692
|
-
async listSessions() {
|
|
4011
|
+
async listSessions(filter) {
|
|
3693
4012
|
if (!this.connection) {
|
|
3694
4013
|
throw new Error("Client not connected");
|
|
3695
4014
|
}
|
|
3696
|
-
const response = await this.connection.sendRequest("session.list", {});
|
|
4015
|
+
const response = await this.connection.sendRequest("session.list", { filter });
|
|
3697
4016
|
const { sessions } = response;
|
|
3698
4017
|
return sessions.map((s) => ({
|
|
3699
4018
|
sessionId: s.sessionId,
|
|
3700
4019
|
startTime: new Date(s.startTime),
|
|
3701
4020
|
modifiedTime: new Date(s.modifiedTime),
|
|
3702
4021
|
summary: s.summary,
|
|
3703
|
-
isRemote: s.isRemote
|
|
4022
|
+
isRemote: s.isRemote,
|
|
4023
|
+
context: s.context
|
|
3704
4024
|
}));
|
|
3705
4025
|
}
|
|
4026
|
+
async getForegroundSessionId() {
|
|
4027
|
+
if (!this.connection) {
|
|
4028
|
+
throw new Error("Client not connected");
|
|
4029
|
+
}
|
|
4030
|
+
const response = await this.connection.sendRequest("session.getForeground", {});
|
|
4031
|
+
return response.sessionId;
|
|
4032
|
+
}
|
|
4033
|
+
async setForegroundSessionId(sessionId) {
|
|
4034
|
+
if (!this.connection) {
|
|
4035
|
+
throw new Error("Client not connected");
|
|
4036
|
+
}
|
|
4037
|
+
const response = await this.connection.sendRequest("session.setForeground", { sessionId });
|
|
4038
|
+
const result = response;
|
|
4039
|
+
if (!result.success) {
|
|
4040
|
+
throw new Error(result.error || "Failed to set foreground session");
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
4043
|
+
on(eventTypeOrHandler, handler) {
|
|
4044
|
+
if (typeof eventTypeOrHandler === "string" && handler) {
|
|
4045
|
+
const eventType = eventTypeOrHandler;
|
|
4046
|
+
if (!this.typedLifecycleHandlers.has(eventType)) {
|
|
4047
|
+
this.typedLifecycleHandlers.set(eventType, /* @__PURE__ */ new Set);
|
|
4048
|
+
}
|
|
4049
|
+
const storedHandler = handler;
|
|
4050
|
+
this.typedLifecycleHandlers.get(eventType).add(storedHandler);
|
|
4051
|
+
return () => {
|
|
4052
|
+
const handlers = this.typedLifecycleHandlers.get(eventType);
|
|
4053
|
+
if (handlers) {
|
|
4054
|
+
handlers.delete(storedHandler);
|
|
4055
|
+
}
|
|
4056
|
+
};
|
|
4057
|
+
}
|
|
4058
|
+
const wildcardHandler = eventTypeOrHandler;
|
|
4059
|
+
this.sessionLifecycleHandlers.add(wildcardHandler);
|
|
4060
|
+
return () => {
|
|
4061
|
+
this.sessionLifecycleHandlers.delete(wildcardHandler);
|
|
4062
|
+
};
|
|
4063
|
+
}
|
|
3706
4064
|
async startCLIServer() {
|
|
3707
4065
|
return new Promise((resolve, reject) => {
|
|
4066
|
+
this.stderrBuffer = "";
|
|
3708
4067
|
const args = [
|
|
3709
4068
|
...this.options.cliArgs,
|
|
3710
|
-
"--
|
|
4069
|
+
"--headless",
|
|
4070
|
+
"--no-auto-update",
|
|
3711
4071
|
"--log-level",
|
|
3712
4072
|
this.options.logLevel
|
|
3713
4073
|
];
|
|
@@ -3716,27 +4076,37 @@ class CopilotClient {
|
|
|
3716
4076
|
} else if (this.options.port > 0) {
|
|
3717
4077
|
args.push("--port", this.options.port.toString());
|
|
3718
4078
|
}
|
|
4079
|
+
if (this.options.githubToken) {
|
|
4080
|
+
args.push("--auth-token-env", "COPILOT_SDK_AUTH_TOKEN");
|
|
4081
|
+
}
|
|
4082
|
+
if (!this.options.useLoggedInUser) {
|
|
4083
|
+
args.push("--no-auto-login");
|
|
4084
|
+
}
|
|
3719
4085
|
const envWithoutNodeDebug = { ...this.options.env };
|
|
3720
4086
|
delete envWithoutNodeDebug.NODE_DEBUG;
|
|
4087
|
+
if (this.options.githubToken) {
|
|
4088
|
+
envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.options.githubToken;
|
|
4089
|
+
}
|
|
4090
|
+
if (!existsSync(this.options.cliPath)) {
|
|
4091
|
+
throw new Error(`Copilot CLI not found at ${this.options.cliPath}. Ensure @github/copilot is installed.`);
|
|
4092
|
+
}
|
|
4093
|
+
const stdioConfig = this.options.useStdio ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"];
|
|
3721
4094
|
const isJsFile = this.options.cliPath.endsWith(".js");
|
|
3722
|
-
const isAbsolutePath = this.options.cliPath.startsWith("/") || /^[a-zA-Z]:/.test(this.options.cliPath);
|
|
3723
|
-
let command;
|
|
3724
|
-
let spawnArgs;
|
|
3725
4095
|
if (isJsFile) {
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
4096
|
+
this.cliProcess = spawn(getNodeExecPath(), [this.options.cliPath, ...args], {
|
|
4097
|
+
stdio: stdioConfig,
|
|
4098
|
+
cwd: this.options.cwd,
|
|
4099
|
+
env: envWithoutNodeDebug,
|
|
4100
|
+
windowsHide: true
|
|
4101
|
+
});
|
|
3731
4102
|
} else {
|
|
3732
|
-
|
|
3733
|
-
|
|
4103
|
+
this.cliProcess = spawn(this.options.cliPath, args, {
|
|
4104
|
+
stdio: stdioConfig,
|
|
4105
|
+
cwd: this.options.cwd,
|
|
4106
|
+
env: envWithoutNodeDebug,
|
|
4107
|
+
windowsHide: true
|
|
4108
|
+
});
|
|
3734
4109
|
}
|
|
3735
|
-
this.cliProcess = spawn(command, spawnArgs, {
|
|
3736
|
-
stdio: this.options.useStdio ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"],
|
|
3737
|
-
cwd: this.options.cwd,
|
|
3738
|
-
env: envWithoutNodeDebug
|
|
3739
|
-
});
|
|
3740
4110
|
let stdout = "";
|
|
3741
4111
|
let resolved = false;
|
|
3742
4112
|
if (this.options.useStdio) {
|
|
@@ -3754,6 +4124,7 @@ class CopilotClient {
|
|
|
3754
4124
|
});
|
|
3755
4125
|
}
|
|
3756
4126
|
this.cliProcess.stderr?.on("data", (data) => {
|
|
4127
|
+
this.stderrBuffer += data.toString();
|
|
3757
4128
|
const lines = data.toString().split(`
|
|
3758
4129
|
`);
|
|
3759
4130
|
for (const line of lines) {
|
|
@@ -3766,13 +4137,39 @@ class CopilotClient {
|
|
|
3766
4137
|
this.cliProcess.on("error", (error) => {
|
|
3767
4138
|
if (!resolved) {
|
|
3768
4139
|
resolved = true;
|
|
3769
|
-
|
|
4140
|
+
const stderrOutput = this.stderrBuffer.trim();
|
|
4141
|
+
if (stderrOutput) {
|
|
4142
|
+
reject(new Error(`Failed to start CLI server: ${error.message}
|
|
4143
|
+
stderr: ${stderrOutput}`));
|
|
4144
|
+
} else {
|
|
4145
|
+
reject(new Error(`Failed to start CLI server: ${error.message}`));
|
|
4146
|
+
}
|
|
3770
4147
|
}
|
|
3771
4148
|
});
|
|
4149
|
+
this.processExitPromise = new Promise((_, rejectProcessExit) => {
|
|
4150
|
+
this.cliProcess.on("exit", (code) => {
|
|
4151
|
+
setTimeout(() => {
|
|
4152
|
+
const stderrOutput = this.stderrBuffer.trim();
|
|
4153
|
+
if (stderrOutput) {
|
|
4154
|
+
rejectProcessExit(new Error(`CLI server exited with code ${code}
|
|
4155
|
+
stderr: ${stderrOutput}`));
|
|
4156
|
+
} else {
|
|
4157
|
+
rejectProcessExit(new Error(`CLI server exited unexpectedly with code ${code}`));
|
|
4158
|
+
}
|
|
4159
|
+
}, 50);
|
|
4160
|
+
});
|
|
4161
|
+
});
|
|
4162
|
+
this.processExitPromise.catch(() => {});
|
|
3772
4163
|
this.cliProcess.on("exit", (code) => {
|
|
3773
4164
|
if (!resolved) {
|
|
3774
4165
|
resolved = true;
|
|
3775
|
-
|
|
4166
|
+
const stderrOutput = this.stderrBuffer.trim();
|
|
4167
|
+
if (stderrOutput) {
|
|
4168
|
+
reject(new Error(`CLI server exited with code ${code}
|
|
4169
|
+
stderr: ${stderrOutput}`));
|
|
4170
|
+
} else {
|
|
4171
|
+
reject(new Error(`CLI server exited with code ${code}`));
|
|
4172
|
+
}
|
|
3776
4173
|
} else if (this.options.autoRestart && this.state === "connected") {
|
|
3777
4174
|
this.reconnect();
|
|
3778
4175
|
}
|
|
@@ -3786,13 +4183,15 @@ class CopilotClient {
|
|
|
3786
4183
|
});
|
|
3787
4184
|
}
|
|
3788
4185
|
async connectToServer() {
|
|
3789
|
-
if (this.options.
|
|
3790
|
-
return this.
|
|
4186
|
+
if (this.options.isChildProcess) {
|
|
4187
|
+
return this.connectToParentProcessViaStdio();
|
|
4188
|
+
} else if (this.options.useStdio) {
|
|
4189
|
+
return this.connectToChildProcessViaStdio();
|
|
3791
4190
|
} else {
|
|
3792
4191
|
return this.connectViaTcp();
|
|
3793
4192
|
}
|
|
3794
4193
|
}
|
|
3795
|
-
async
|
|
4194
|
+
async connectToChildProcessViaStdio() {
|
|
3796
4195
|
if (!this.cliProcess) {
|
|
3797
4196
|
throw new Error("CLI process not started");
|
|
3798
4197
|
}
|
|
@@ -3801,7 +4200,15 @@ class CopilotClient {
|
|
|
3801
4200
|
throw err;
|
|
3802
4201
|
}
|
|
3803
4202
|
});
|
|
3804
|
-
this.connection =
|
|
4203
|
+
this.connection = import_node2.createMessageConnection(new import_node2.StreamMessageReader(this.cliProcess.stdout), new import_node2.StreamMessageWriter(this.cliProcess.stdin));
|
|
4204
|
+
this.attachConnectionHandlers();
|
|
4205
|
+
this.connection.listen();
|
|
4206
|
+
}
|
|
4207
|
+
async connectToParentProcessViaStdio() {
|
|
4208
|
+
if (this.cliProcess) {
|
|
4209
|
+
throw new Error("CLI child process was unexpectedly started in parent process mode");
|
|
4210
|
+
}
|
|
4211
|
+
this.connection = import_node2.createMessageConnection(new import_node2.StreamMessageReader(process.stdin), new import_node2.StreamMessageWriter(process.stdout));
|
|
3805
4212
|
this.attachConnectionHandlers();
|
|
3806
4213
|
this.connection.listen();
|
|
3807
4214
|
}
|
|
@@ -3812,7 +4219,7 @@ class CopilotClient {
|
|
|
3812
4219
|
return new Promise((resolve, reject) => {
|
|
3813
4220
|
this.socket = new Socket;
|
|
3814
4221
|
this.socket.connect(this.actualPort, this.actualHost, () => {
|
|
3815
|
-
this.connection =
|
|
4222
|
+
this.connection = import_node2.createMessageConnection(new import_node2.StreamMessageReader(this.socket), new import_node2.StreamMessageWriter(this.socket));
|
|
3816
4223
|
this.attachConnectionHandlers();
|
|
3817
4224
|
this.connection.listen();
|
|
3818
4225
|
resolve();
|
|
@@ -3829,8 +4236,13 @@ class CopilotClient {
|
|
|
3829
4236
|
this.connection.onNotification("session.event", (notification) => {
|
|
3830
4237
|
this.handleSessionEventNotification(notification);
|
|
3831
4238
|
});
|
|
3832
|
-
this.connection.
|
|
3833
|
-
|
|
4239
|
+
this.connection.onNotification("session.lifecycle", (notification) => {
|
|
4240
|
+
this.handleSessionLifecycleNotification(notification);
|
|
4241
|
+
});
|
|
4242
|
+
this.connection.onRequest("tool.call", async (params) => await this.handleToolCallRequestV2(params));
|
|
4243
|
+
this.connection.onRequest("permission.request", async (params) => await this.handlePermissionRequestV2(params));
|
|
4244
|
+
this.connection.onRequest("userInput.request", async (params) => await this.handleUserInputRequest(params));
|
|
4245
|
+
this.connection.onRequest("hooks.invoke", async (params) => await this.handleHooksInvoke(params));
|
|
3834
4246
|
this.connection.onClose(() => {
|
|
3835
4247
|
if (this.state === "connected" && this.options.autoRestart) {
|
|
3836
4248
|
this.reconnect();
|
|
@@ -3847,7 +4259,52 @@ class CopilotClient {
|
|
|
3847
4259
|
session._dispatchEvent(notification.event);
|
|
3848
4260
|
}
|
|
3849
4261
|
}
|
|
3850
|
-
|
|
4262
|
+
handleSessionLifecycleNotification(notification) {
|
|
4263
|
+
if (typeof notification !== "object" || !notification || !("type" in notification) || typeof notification.type !== "string" || !("sessionId" in notification) || typeof notification.sessionId !== "string") {
|
|
4264
|
+
return;
|
|
4265
|
+
}
|
|
4266
|
+
const event = notification;
|
|
4267
|
+
const typedHandlers = this.typedLifecycleHandlers.get(event.type);
|
|
4268
|
+
if (typedHandlers) {
|
|
4269
|
+
for (const handler of typedHandlers) {
|
|
4270
|
+
try {
|
|
4271
|
+
handler(event);
|
|
4272
|
+
} catch {}
|
|
4273
|
+
}
|
|
4274
|
+
}
|
|
4275
|
+
for (const handler of this.sessionLifecycleHandlers) {
|
|
4276
|
+
try {
|
|
4277
|
+
handler(event);
|
|
4278
|
+
} catch {}
|
|
4279
|
+
}
|
|
4280
|
+
}
|
|
4281
|
+
async handleUserInputRequest(params) {
|
|
4282
|
+
if (!params || typeof params.sessionId !== "string" || typeof params.question !== "string") {
|
|
4283
|
+
throw new Error("Invalid user input request payload");
|
|
4284
|
+
}
|
|
4285
|
+
const session = this.sessions.get(params.sessionId);
|
|
4286
|
+
if (!session) {
|
|
4287
|
+
throw new Error(`Session not found: ${params.sessionId}`);
|
|
4288
|
+
}
|
|
4289
|
+
const result = await session._handleUserInputRequest({
|
|
4290
|
+
question: params.question,
|
|
4291
|
+
choices: params.choices,
|
|
4292
|
+
allowFreeform: params.allowFreeform
|
|
4293
|
+
});
|
|
4294
|
+
return result;
|
|
4295
|
+
}
|
|
4296
|
+
async handleHooksInvoke(params) {
|
|
4297
|
+
if (!params || typeof params.sessionId !== "string" || typeof params.hookType !== "string") {
|
|
4298
|
+
throw new Error("Invalid hooks invoke payload");
|
|
4299
|
+
}
|
|
4300
|
+
const session = this.sessions.get(params.sessionId);
|
|
4301
|
+
if (!session) {
|
|
4302
|
+
throw new Error(`Session not found: ${params.sessionId}`);
|
|
4303
|
+
}
|
|
4304
|
+
const output = await session._handleHooksInvoke(params.hookType, params.input);
|
|
4305
|
+
return { output };
|
|
4306
|
+
}
|
|
4307
|
+
async handleToolCallRequestV2(params) {
|
|
3851
4308
|
if (!params || typeof params.sessionId !== "string" || typeof params.toolCallId !== "string" || typeof params.toolName !== "string") {
|
|
3852
4309
|
throw new Error("Invalid tool call payload");
|
|
3853
4310
|
}
|
|
@@ -3857,20 +4314,24 @@ class CopilotClient {
|
|
|
3857
4314
|
}
|
|
3858
4315
|
const handler = session.getToolHandler(params.toolName);
|
|
3859
4316
|
if (!handler) {
|
|
3860
|
-
return {
|
|
4317
|
+
return {
|
|
4318
|
+
result: {
|
|
4319
|
+
textResultForLlm: `Tool '${params.toolName}' is not supported by this client instance.`,
|
|
4320
|
+
resultType: "failure",
|
|
4321
|
+
error: `tool '${params.toolName}' not supported`,
|
|
4322
|
+
toolTelemetry: {}
|
|
4323
|
+
}
|
|
4324
|
+
};
|
|
3861
4325
|
}
|
|
3862
|
-
return await this.executeToolCall(handler, params);
|
|
3863
|
-
}
|
|
3864
|
-
async executeToolCall(handler, request) {
|
|
3865
4326
|
try {
|
|
3866
4327
|
const invocation = {
|
|
3867
|
-
sessionId:
|
|
3868
|
-
toolCallId:
|
|
3869
|
-
toolName:
|
|
3870
|
-
arguments:
|
|
4328
|
+
sessionId: params.sessionId,
|
|
4329
|
+
toolCallId: params.toolCallId,
|
|
4330
|
+
toolName: params.toolName,
|
|
4331
|
+
arguments: params.arguments
|
|
3871
4332
|
};
|
|
3872
|
-
const result = await handler(
|
|
3873
|
-
return { result: this.
|
|
4333
|
+
const result = await handler(params.arguments, invocation);
|
|
4334
|
+
return { result: this.normalizeToolResultV2(result) };
|
|
3874
4335
|
} catch (error) {
|
|
3875
4336
|
const message = error instanceof Error ? error.message : String(error);
|
|
3876
4337
|
return {
|
|
@@ -3883,7 +4344,7 @@ class CopilotClient {
|
|
|
3883
4344
|
};
|
|
3884
4345
|
}
|
|
3885
4346
|
}
|
|
3886
|
-
async
|
|
4347
|
+
async handlePermissionRequestV2(params) {
|
|
3887
4348
|
if (!params || typeof params.sessionId !== "string" || !params.permissionRequest) {
|
|
3888
4349
|
throw new Error("Invalid permission request payload");
|
|
3889
4350
|
}
|
|
@@ -3892,7 +4353,7 @@ class CopilotClient {
|
|
|
3892
4353
|
throw new Error(`Session not found: ${params.sessionId}`);
|
|
3893
4354
|
}
|
|
3894
4355
|
try {
|
|
3895
|
-
const result = await session.
|
|
4356
|
+
const result = await session._handlePermissionRequestV2(params.permissionRequest);
|
|
3896
4357
|
return { result };
|
|
3897
4358
|
} catch (_error) {
|
|
3898
4359
|
return {
|
|
@@ -3902,7 +4363,7 @@ class CopilotClient {
|
|
|
3902
4363
|
};
|
|
3903
4364
|
}
|
|
3904
4365
|
}
|
|
3905
|
-
|
|
4366
|
+
normalizeToolResultV2(result) {
|
|
3906
4367
|
if (result === undefined || result === null) {
|
|
3907
4368
|
return {
|
|
3908
4369
|
textResultForLlm: "Tool returned no result",
|
|
@@ -3924,14 +4385,6 @@ class CopilotClient {
|
|
|
3924
4385
|
isToolResultObject(value) {
|
|
3925
4386
|
return typeof value === "object" && value !== null && "textResultForLlm" in value && typeof value.textResultForLlm === "string" && "resultType" in value;
|
|
3926
4387
|
}
|
|
3927
|
-
buildUnsupportedToolResult(toolName) {
|
|
3928
|
-
return {
|
|
3929
|
-
textResultForLlm: `Tool '${toolName}' is not supported by this client instance.`,
|
|
3930
|
-
resultType: "failure",
|
|
3931
|
-
error: `tool '${toolName}' not supported`,
|
|
3932
|
-
toolTelemetry: {}
|
|
3933
|
-
};
|
|
3934
|
-
}
|
|
3935
4388
|
async reconnect() {
|
|
3936
4389
|
this.state = "disconnected";
|
|
3937
4390
|
try {
|
|
@@ -4616,7 +5069,8 @@ async function runSubtitleCorrectionAgent(config) {
|
|
|
4616
5069
|
if (eventLogPath) {
|
|
4617
5070
|
console.log(`偵錯: Copilot events 記錄到 ${eventLogPath}`);
|
|
4618
5071
|
}
|
|
4619
|
-
const client = new CopilotClient;
|
|
5072
|
+
const client = new CopilotClient({ cliArgs: ["--allow-all-paths"] });
|
|
5073
|
+
const autoApprovePermission = async () => ({ kind: "approved" });
|
|
4620
5074
|
try {
|
|
4621
5075
|
await client.start();
|
|
4622
5076
|
const azureEndpoint = process.env.AZURE_OPENAI_ENDPOINT;
|
|
@@ -4642,6 +5096,7 @@ async function runSubtitleCorrectionAgent(config) {
|
|
|
4642
5096
|
}
|
|
4643
5097
|
session = await client.createSession({
|
|
4644
5098
|
model: azureDeployment,
|
|
5099
|
+
onPermissionRequest: autoApprovePermission,
|
|
4645
5100
|
streaming: true,
|
|
4646
5101
|
provider: {
|
|
4647
5102
|
type: "azure",
|
|
@@ -4660,6 +5115,7 @@ async function runSubtitleCorrectionAgent(config) {
|
|
|
4660
5115
|
}
|
|
4661
5116
|
session = await client.createSession({
|
|
4662
5117
|
model: "gpt-4o",
|
|
5118
|
+
onPermissionRequest: autoApprovePermission,
|
|
4663
5119
|
streaming: true,
|
|
4664
5120
|
provider: {
|
|
4665
5121
|
type: "openai",
|
|
@@ -4671,6 +5127,7 @@ async function runSubtitleCorrectionAgent(config) {
|
|
|
4671
5127
|
} else {
|
|
4672
5128
|
console.log("使用 GitHub Copilot 作為模型提供者");
|
|
4673
5129
|
session = await client.createSession({
|
|
5130
|
+
onPermissionRequest: autoApprovePermission,
|
|
4674
5131
|
tools
|
|
4675
5132
|
});
|
|
4676
5133
|
}
|
|
@@ -4962,7 +5419,7 @@ function parseArgs(args) {
|
|
|
4962
5419
|
return result;
|
|
4963
5420
|
}
|
|
4964
5421
|
function resolveProjectRoot() {
|
|
4965
|
-
const currentFile =
|
|
5422
|
+
const currentFile = fileURLToPath2(import.meta.url);
|
|
4966
5423
|
let dir = path6.dirname(currentFile);
|
|
4967
5424
|
while (true) {
|
|
4968
5425
|
const candidate = path6.join(dir, "package.json");
|
|
@@ -5147,7 +5604,7 @@ var isDirectRun = (() => {
|
|
|
5147
5604
|
if (typeof metaMain === "boolean") {
|
|
5148
5605
|
return metaMain;
|
|
5149
5606
|
}
|
|
5150
|
-
const entryPath =
|
|
5607
|
+
const entryPath = fileURLToPath2(import.meta.url);
|
|
5151
5608
|
return Boolean(process.argv[1]) && path6.resolve(process.argv[1]) === path6.resolve(entryPath);
|
|
5152
5609
|
})();
|
|
5153
5610
|
if (isDirectRun) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@willh/subtitle-correction-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "影片字幕校正代理人 - 使用 GitHub Copilot SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -9,30 +9,30 @@
|
|
|
9
9
|
"bin": {
|
|
10
10
|
"subtitle-correction-agent": "dist/index.js"
|
|
11
11
|
},
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
12
|
+
"main": "dist/index.js",
|
|
13
|
+
"scripts": {
|
|
14
|
+
"bump": "npm version patch --no-git-tag-version && npm install --package-lock-only",
|
|
15
|
+
"build": "bun build src/index.ts --outdir dist --target node --format esm",
|
|
16
|
+
"start": "npm run build && bun run src/index.ts",
|
|
17
|
+
"dev": "npm run build && bun run src/index.ts",
|
|
18
|
+
"prepack": "npm run build",
|
|
19
|
+
"test": "bun test",
|
|
20
|
+
"typecheck": "tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"subtitle",
|
|
24
|
+
"vtt",
|
|
25
|
+
"correction",
|
|
26
|
+
"copilot-sdk",
|
|
27
|
+
"agent"
|
|
28
|
+
],
|
|
29
|
+
"author": "Will 保哥",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@github/copilot-sdk": "^0.1.32"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^20.11.0",
|
|
36
|
+
"typescript": "^5.3.3"
|
|
38
37
|
}
|
|
38
|
+
}
|