@ai-development-environment/control-agent 0.0.90 → 0.0.92
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/control-agent.js +188 -10
- package/package.json +1 -1
package/dist/control-agent.js
CHANGED
|
@@ -6172,6 +6172,7 @@ var WORKTREE_OPERATIONS = [
|
|
|
6172
6172
|
"PULL",
|
|
6173
6173
|
"PUSH",
|
|
6174
6174
|
"RESET",
|
|
6175
|
+
"CLEAR_LOCK",
|
|
6175
6176
|
"STASH_ALL",
|
|
6176
6177
|
"STAGE_ALL",
|
|
6177
6178
|
"UNSTAGE_ALL"
|
|
@@ -14355,6 +14356,20 @@ function rootPath(location) {
|
|
|
14355
14356
|
}
|
|
14356
14357
|
return path;
|
|
14357
14358
|
}
|
|
14359
|
+
function isContainedPath(base, candidate) {
|
|
14360
|
+
return candidate === base || candidate.startsWith(`${base}${sep6}`);
|
|
14361
|
+
}
|
|
14362
|
+
async function resolvedSkillRoot(location) {
|
|
14363
|
+
const root = rootPath(location);
|
|
14364
|
+
const resolvedRoot = await realpath6(root);
|
|
14365
|
+
if (location.scope === "PROJECT") {
|
|
14366
|
+
const resolvedProject = await realpath6(location.folder);
|
|
14367
|
+
if (!isContainedPath(resolvedProject, resolvedRoot)) {
|
|
14368
|
+
throw new Error("Project skill root resolves outside the project folder");
|
|
14369
|
+
}
|
|
14370
|
+
}
|
|
14371
|
+
return { root, resolvedRoot };
|
|
14372
|
+
}
|
|
14358
14373
|
function targetForFolder(targets, folder) {
|
|
14359
14374
|
return folder ? targets.find((target2) => resolve6(target2.folder) === resolve6(folder)) : void 0;
|
|
14360
14375
|
}
|
|
@@ -14395,14 +14410,15 @@ async function listPackageFiles(skillDirectory) {
|
|
|
14395
14410
|
await visit(skillDirectory);
|
|
14396
14411
|
return files;
|
|
14397
14412
|
}
|
|
14398
|
-
async function readPackage(directory) {
|
|
14413
|
+
async function readPackage(directory, resolvedRoot) {
|
|
14399
14414
|
const metadata = await lstat3(directory);
|
|
14400
|
-
|
|
14401
|
-
if (metadata.isSymbolicLink()) {
|
|
14402
|
-
resolvedDirectory = await realpath6(directory);
|
|
14403
|
-
} else if (!metadata.isDirectory()) {
|
|
14415
|
+
if (!metadata.isSymbolicLink() && !metadata.isDirectory()) {
|
|
14404
14416
|
throw new Error("Skill path is not a directory");
|
|
14405
14417
|
}
|
|
14418
|
+
const resolvedDirectory = await realpath6(directory);
|
|
14419
|
+
if (!isContainedPath(resolvedRoot, resolvedDirectory)) {
|
|
14420
|
+
throw new Error("Skill package resolves outside its configured root");
|
|
14421
|
+
}
|
|
14406
14422
|
const files = await listPackageFiles(resolvedDirectory);
|
|
14407
14423
|
const definition = files.find((file) => file.path === "SKILL.md");
|
|
14408
14424
|
if (!definition) throw new Error("Skill does not contain SKILL.md");
|
|
@@ -14442,6 +14458,15 @@ function consumersForRoot(configured, rootKind, scope) {
|
|
|
14442
14458
|
async function scanRoot2(location, configured, targets, warnings) {
|
|
14443
14459
|
const root = rootPath(location);
|
|
14444
14460
|
if (!await directoryExists(root)) return [];
|
|
14461
|
+
let resolvedRoot;
|
|
14462
|
+
try {
|
|
14463
|
+
resolvedRoot = (await resolvedSkillRoot(location)).resolvedRoot;
|
|
14464
|
+
} catch (error) {
|
|
14465
|
+
warnings.push(
|
|
14466
|
+
`${root}: ${error instanceof Error ? error.message : String(error)}`
|
|
14467
|
+
);
|
|
14468
|
+
return [];
|
|
14469
|
+
}
|
|
14445
14470
|
const consumers = consumersForRoot(
|
|
14446
14471
|
configured,
|
|
14447
14472
|
location.rootKind,
|
|
@@ -14454,7 +14479,7 @@ async function scanRoot2(location, configured, targets, warnings) {
|
|
|
14454
14479
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
14455
14480
|
const directory = join9(root, entry.name);
|
|
14456
14481
|
try {
|
|
14457
|
-
const skillPackage = await readPackage(directory);
|
|
14482
|
+
const skillPackage = await readPackage(directory, resolvedRoot);
|
|
14458
14483
|
const relativeDirectory = location.folder ? relative6(location.folder, directory).split(sep6).join("/") : "";
|
|
14459
14484
|
installations.push({
|
|
14460
14485
|
...location,
|
|
@@ -14529,9 +14554,10 @@ var readSkills = async (payload) => {
|
|
|
14529
14554
|
if (!allowed.has(`${request.scope}:${root}`)) {
|
|
14530
14555
|
throw new Error("Requested skill root is not enabled or configured");
|
|
14531
14556
|
}
|
|
14557
|
+
const { resolvedRoot } = await resolvedSkillRoot(request);
|
|
14532
14558
|
packages.push({
|
|
14533
14559
|
...request,
|
|
14534
|
-
package: await readPackage(join9(root, request.skillName))
|
|
14560
|
+
package: await readPackage(join9(root, request.skillName), resolvedRoot)
|
|
14535
14561
|
});
|
|
14536
14562
|
}
|
|
14537
14563
|
return { ...successfulProcess6, packages };
|
|
@@ -18012,6 +18038,9 @@ var operateWorktree = async (payload, timeoutMs, signal) => {
|
|
|
18012
18038
|
await runGit(["clean", "-fd"], "Could not remove untracked files");
|
|
18013
18039
|
break;
|
|
18014
18040
|
}
|
|
18041
|
+
case "CLEAR_LOCK":
|
|
18042
|
+
await rm10(join14(input.gitDirectory, "index.lock"), { force: true });
|
|
18043
|
+
break;
|
|
18015
18044
|
case "STASH_ALL":
|
|
18016
18045
|
await runGit(["stash", "push", "--include-untracked"], "Stash failed");
|
|
18017
18046
|
break;
|
|
@@ -51216,6 +51245,89 @@ function opencodeQuestionResolution(value) {
|
|
|
51216
51245
|
const requestId = data.requestID ?? properties.requestID;
|
|
51217
51246
|
return typeof requestId === "string" ? requestId : void 0;
|
|
51218
51247
|
}
|
|
51248
|
+
function stringArray3(value) {
|
|
51249
|
+
return Array.isArray(value) ? value.map(String) : [];
|
|
51250
|
+
}
|
|
51251
|
+
function opencodePermissionRequest(value, fallbackSurface) {
|
|
51252
|
+
const event = asRecord(value);
|
|
51253
|
+
const payload = asRecord(event.payload);
|
|
51254
|
+
const type = String(payload.type ?? event.type ?? "");
|
|
51255
|
+
if (type && type !== "permission.asked" && type !== "permission.v2.asked")
|
|
51256
|
+
return null;
|
|
51257
|
+
const properties = asRecord(payload.properties ?? event.properties);
|
|
51258
|
+
const request = type ? asRecord(
|
|
51259
|
+
properties.request ?? properties.data ?? payload.data ?? event.data ?? properties
|
|
51260
|
+
) : event;
|
|
51261
|
+
const permission = request.permission ?? request.action;
|
|
51262
|
+
if (typeof permission !== "string") return null;
|
|
51263
|
+
return {
|
|
51264
|
+
id: String(request.id ?? properties.id ?? "permission"),
|
|
51265
|
+
sessionId: typeof request.sessionID === "string" ? request.sessionID : typeof properties.sessionID === "string" ? properties.sessionID : void 0,
|
|
51266
|
+
surface: type === "permission.v2.asked" ? "V2" : fallbackSurface ?? "LEGACY",
|
|
51267
|
+
permission,
|
|
51268
|
+
patterns: stringArray3(request.patterns ?? request.resources),
|
|
51269
|
+
always: stringArray3(request.always ?? request.save)
|
|
51270
|
+
};
|
|
51271
|
+
}
|
|
51272
|
+
function opencodePermissionResolution(value) {
|
|
51273
|
+
const event = asRecord(value);
|
|
51274
|
+
const payload = asRecord(event.payload);
|
|
51275
|
+
const type = String(payload.type ?? event.type ?? "");
|
|
51276
|
+
if (type !== "permission.replied" && type !== "permission.v2.replied")
|
|
51277
|
+
return void 0;
|
|
51278
|
+
const properties = asRecord(payload.properties ?? event.properties);
|
|
51279
|
+
const data = asRecord(payload.data ?? event.data ?? properties.data);
|
|
51280
|
+
const requestId = data.requestID ?? properties.requestID;
|
|
51281
|
+
return typeof requestId === "string" ? requestId : void 0;
|
|
51282
|
+
}
|
|
51283
|
+
function permissionQuestions(request) {
|
|
51284
|
+
const permission = request.permission.replaceAll("_", " ");
|
|
51285
|
+
const targets = request.patterns.length ? ` for ${request.patterns.join(", ")}` : "";
|
|
51286
|
+
return [
|
|
51287
|
+
{
|
|
51288
|
+
id: "permission",
|
|
51289
|
+
header: "Permission required",
|
|
51290
|
+
prompt: `OpenCode requests ${permission} permission${targets}.`,
|
|
51291
|
+
multiSelect: false,
|
|
51292
|
+
allowCustom: false,
|
|
51293
|
+
options: [
|
|
51294
|
+
{
|
|
51295
|
+
label: "Allow once",
|
|
51296
|
+
description: "Approve only this request."
|
|
51297
|
+
},
|
|
51298
|
+
...request.always.length ? [
|
|
51299
|
+
{
|
|
51300
|
+
label: "Always allow",
|
|
51301
|
+
description: `Approve this request and remember ${request.always.join(", ")}.`
|
|
51302
|
+
}
|
|
51303
|
+
] : [],
|
|
51304
|
+
{
|
|
51305
|
+
label: "Reject",
|
|
51306
|
+
description: "Deny this request and let OpenCode continue safely."
|
|
51307
|
+
}
|
|
51308
|
+
]
|
|
51309
|
+
}
|
|
51310
|
+
];
|
|
51311
|
+
}
|
|
51312
|
+
function permissionReply(value) {
|
|
51313
|
+
const selected = answerArrays(value).flat().map((answer) => answer.trim().toLowerCase()).filter(Boolean);
|
|
51314
|
+
if (selected.length !== 1)
|
|
51315
|
+
throw new Error("Select one response for the OpenCode permission request");
|
|
51316
|
+
switch (selected[0]) {
|
|
51317
|
+
case "allow once":
|
|
51318
|
+
case "once":
|
|
51319
|
+
return "once";
|
|
51320
|
+
case "always allow":
|
|
51321
|
+
case "allow always":
|
|
51322
|
+
case "always":
|
|
51323
|
+
return "always";
|
|
51324
|
+
case "reject":
|
|
51325
|
+
case "deny":
|
|
51326
|
+
return "reject";
|
|
51327
|
+
default:
|
|
51328
|
+
throw new Error(`Unknown OpenCode permission response: ${selected[0]}`);
|
|
51329
|
+
}
|
|
51330
|
+
}
|
|
51219
51331
|
function responseItems(value) {
|
|
51220
51332
|
const data = resultData(value);
|
|
51221
51333
|
if (Array.isArray(data)) return data;
|
|
@@ -51344,6 +51456,7 @@ var OpenCodeAdapter = class {
|
|
|
51344
51456
|
let stopReason = null;
|
|
51345
51457
|
const streamController = new AbortController();
|
|
51346
51458
|
const questionSurfaces = /* @__PURE__ */ new Map();
|
|
51459
|
+
const permissionSurfaces = /* @__PURE__ */ new Map();
|
|
51347
51460
|
const pendingQuestions = /* @__PURE__ */ new Set();
|
|
51348
51461
|
let sawQuestion = false;
|
|
51349
51462
|
const reportQuestion = async (request) => {
|
|
@@ -51359,6 +51472,19 @@ var OpenCodeAdapter = class {
|
|
|
51359
51472
|
throw error;
|
|
51360
51473
|
}
|
|
51361
51474
|
};
|
|
51475
|
+
const reportPermission = async (request) => {
|
|
51476
|
+
if (permissionSurfaces.has(request.id)) return;
|
|
51477
|
+
permissionSurfaces.set(request.id, request.surface);
|
|
51478
|
+
pendingQuestions.add(request.id);
|
|
51479
|
+
sawQuestion = true;
|
|
51480
|
+
try {
|
|
51481
|
+
await callbacks.onQuestion(request.id, permissionQuestions(request));
|
|
51482
|
+
} catch (error) {
|
|
51483
|
+
permissionSurfaces.delete(request.id);
|
|
51484
|
+
pendingQuestions.delete(request.id);
|
|
51485
|
+
throw error;
|
|
51486
|
+
}
|
|
51487
|
+
};
|
|
51362
51488
|
const reconcileLegacyQuestions = async () => {
|
|
51363
51489
|
const response = await client2.question.list(
|
|
51364
51490
|
{ directory: cwd },
|
|
@@ -51381,6 +51507,27 @@ var OpenCodeAdapter = class {
|
|
|
51381
51507
|
await reportQuestion(request);
|
|
51382
51508
|
}
|
|
51383
51509
|
};
|
|
51510
|
+
const reconcileLegacyPermissions = async () => {
|
|
51511
|
+
const response = await client2.permission.list(
|
|
51512
|
+
{ directory: cwd },
|
|
51513
|
+
{ signal: AbortSignal.timeout(QUESTION_RECONCILIATION_TIMEOUT_MS) }
|
|
51514
|
+
);
|
|
51515
|
+
for (const value of responseItems(response)) {
|
|
51516
|
+
const request = opencodePermissionRequest(value, "LEGACY");
|
|
51517
|
+
if (request?.sessionId === nativeId) await reportPermission(request);
|
|
51518
|
+
}
|
|
51519
|
+
};
|
|
51520
|
+
const reconcileV2Permissions = async () => {
|
|
51521
|
+
const response = await client2.v2.session.permission.list(
|
|
51522
|
+
{ sessionID: nativeId },
|
|
51523
|
+
{ signal: AbortSignal.timeout(QUESTION_RECONCILIATION_TIMEOUT_MS) }
|
|
51524
|
+
);
|
|
51525
|
+
for (const value of responseItems(response)) {
|
|
51526
|
+
const request = opencodePermissionRequest(value, "V2");
|
|
51527
|
+
if (request && (!request.sessionId || request.sessionId === nativeId))
|
|
51528
|
+
await reportPermission(request);
|
|
51529
|
+
}
|
|
51530
|
+
};
|
|
51384
51531
|
const eventTask = (async () => {
|
|
51385
51532
|
try {
|
|
51386
51533
|
const subscription = await client2.event.subscribe(
|
|
@@ -51391,8 +51538,13 @@ var OpenCodeAdapter = class {
|
|
|
51391
51538
|
if (eventSessionId(event) !== nativeId) continue;
|
|
51392
51539
|
const question = opencodeQuestionRequest(event);
|
|
51393
51540
|
if (question) await reportQuestion(question);
|
|
51541
|
+
const permission = opencodePermissionRequest(event);
|
|
51542
|
+
if (permission) await reportPermission(permission);
|
|
51394
51543
|
const resolvedRequestId = opencodeQuestionResolution(event);
|
|
51395
51544
|
if (resolvedRequestId) pendingQuestions.delete(resolvedRequestId);
|
|
51545
|
+
const resolvedPermissionId = opencodePermissionResolution(event);
|
|
51546
|
+
if (resolvedPermissionId)
|
|
51547
|
+
pendingQuestions.delete(resolvedPermissionId);
|
|
51396
51548
|
const record = asRecord(event);
|
|
51397
51549
|
const payload = asRecord(record.payload);
|
|
51398
51550
|
const type = String(payload.type ?? record.type ?? "event");
|
|
@@ -51446,6 +51598,14 @@ var OpenCodeAdapter = class {
|
|
|
51446
51598
|
"V2",
|
|
51447
51599
|
reconcileV2Questions
|
|
51448
51600
|
);
|
|
51601
|
+
const legacyPermissionReconciliationTask = questionReconciliationTask(
|
|
51602
|
+
"LEGACY permission",
|
|
51603
|
+
reconcileLegacyPermissions
|
|
51604
|
+
);
|
|
51605
|
+
const v2PermissionReconciliationTask = questionReconciliationTask(
|
|
51606
|
+
"V2 permission",
|
|
51607
|
+
reconcileV2Permissions
|
|
51608
|
+
);
|
|
51449
51609
|
const send = async (prompt, attachments) => {
|
|
51450
51610
|
const response = await client2.session.prompt({
|
|
51451
51611
|
sessionID: nativeId,
|
|
@@ -51471,7 +51631,9 @@ var OpenCodeAdapter = class {
|
|
|
51471
51631
|
let response = await send(input.prompt, input.attachments);
|
|
51472
51632
|
await Promise.allSettled([
|
|
51473
51633
|
reconcileLegacyQuestions(),
|
|
51474
|
-
reconcileV2Questions()
|
|
51634
|
+
reconcileV2Questions(),
|
|
51635
|
+
reconcileLegacyPermissions(),
|
|
51636
|
+
reconcileV2Permissions()
|
|
51475
51637
|
]);
|
|
51476
51638
|
while (!stopReason && pendingQuestions.size) {
|
|
51477
51639
|
await waitForPoll(streamController.signal);
|
|
@@ -51518,7 +51680,9 @@ var OpenCodeAdapter = class {
|
|
|
51518
51680
|
await Promise.allSettled([
|
|
51519
51681
|
eventTask,
|
|
51520
51682
|
legacyQuestionReconciliationTask,
|
|
51521
|
-
v2QuestionReconciliationTask
|
|
51683
|
+
v2QuestionReconciliationTask,
|
|
51684
|
+
legacyPermissionReconciliationTask,
|
|
51685
|
+
v2PermissionReconciliationTask
|
|
51522
51686
|
]);
|
|
51523
51687
|
isolatedRuntime?.server.close();
|
|
51524
51688
|
}
|
|
@@ -51534,7 +51698,20 @@ var OpenCodeAdapter = class {
|
|
|
51534
51698
|
await send(prompt, attachments);
|
|
51535
51699
|
},
|
|
51536
51700
|
async answer(requestId, answers) {
|
|
51537
|
-
|
|
51701
|
+
const permissionSurface = permissionSurfaces.get(requestId);
|
|
51702
|
+
if (permissionSurface === "V2") {
|
|
51703
|
+
await client2.v2.session.permission.reply({
|
|
51704
|
+
sessionID: nativeId,
|
|
51705
|
+
requestID: requestId,
|
|
51706
|
+
reply: permissionReply(answers)
|
|
51707
|
+
});
|
|
51708
|
+
} else if (permissionSurface === "LEGACY") {
|
|
51709
|
+
await client2.permission.reply({
|
|
51710
|
+
requestID: requestId,
|
|
51711
|
+
directory: cwd,
|
|
51712
|
+
reply: permissionReply(answers)
|
|
51713
|
+
});
|
|
51714
|
+
} else if (questionSurfaces.get(requestId) === "V2") {
|
|
51538
51715
|
await client2.v2.session.question.reply({
|
|
51539
51716
|
sessionID: nativeId,
|
|
51540
51717
|
requestID: requestId,
|
|
@@ -51548,6 +51725,7 @@ var OpenCodeAdapter = class {
|
|
|
51548
51725
|
});
|
|
51549
51726
|
}
|
|
51550
51727
|
pendingQuestions.delete(requestId);
|
|
51728
|
+
permissionSurfaces.delete(requestId);
|
|
51551
51729
|
}
|
|
51552
51730
|
};
|
|
51553
51731
|
}
|