opencode-gitlab-duo-agentic-custom-tools 0.3.2 → 0.3.4
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 +246 -31
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -359,16 +359,12 @@ async function createPluginHooks(input) {
|
|
|
359
359
|
always: ["*"],
|
|
360
360
|
metadata: {}
|
|
361
361
|
});
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
if (Array.isArray(payload)) {
|
|
369
|
-
return JSON.stringify(payload, null, 2);
|
|
370
|
-
}
|
|
371
|
-
return "[]";
|
|
362
|
+
const response = await input.client.session.todo({
|
|
363
|
+
path: { id: ctx.sessionID },
|
|
364
|
+
throwOnError: true
|
|
365
|
+
});
|
|
366
|
+
const payload = response.data ?? [];
|
|
367
|
+
return JSON.stringify(payload, null, 2);
|
|
372
368
|
}
|
|
373
369
|
})
|
|
374
370
|
},
|
|
@@ -1268,8 +1264,12 @@ function isPlainObject(value) {
|
|
|
1268
1264
|
// src/provider/tool-mapping.ts
|
|
1269
1265
|
var TODO_WRITE_PROGRAM = "__todo_write__";
|
|
1270
1266
|
var TODO_READ_PROGRAM = "__todo_read__";
|
|
1267
|
+
var WEBFETCH_PROGRAM = "__webfetch__";
|
|
1268
|
+
var QUESTION_PROGRAM = "__question__";
|
|
1269
|
+
var SKILL_PROGRAM = "__skill__";
|
|
1271
1270
|
var TODO_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress", "completed", "cancelled"]);
|
|
1272
1271
|
var TODO_PRIORITIES = /* @__PURE__ */ new Set(["high", "medium", "low"]);
|
|
1272
|
+
var WEBFETCH_FORMATS = /* @__PURE__ */ new Set(["text", "markdown", "html"]);
|
|
1273
1273
|
function mapDuoToolRequest(toolName, args) {
|
|
1274
1274
|
switch (toolName) {
|
|
1275
1275
|
case "list_dir": {
|
|
@@ -1325,14 +1325,14 @@ function mapDuoToolRequest(toolName, args) {
|
|
|
1325
1325
|
case "shell_command": {
|
|
1326
1326
|
const command = asString2(args.command);
|
|
1327
1327
|
if (!command) return { toolName, args };
|
|
1328
|
-
const bridged =
|
|
1328
|
+
const bridged = mapBridgeCommand(command);
|
|
1329
1329
|
if (bridged) return bridged;
|
|
1330
1330
|
return { toolName: "bash", args: { command, description: "Run shell command", workdir: "." } };
|
|
1331
1331
|
}
|
|
1332
1332
|
case "run_command": {
|
|
1333
1333
|
const command = asString2(args.command);
|
|
1334
1334
|
if (command) {
|
|
1335
|
-
const bridged =
|
|
1335
|
+
const bridged = mapBridgeCommand(command);
|
|
1336
1336
|
if (bridged) return bridged;
|
|
1337
1337
|
}
|
|
1338
1338
|
const program = asString2(args.program);
|
|
@@ -1342,6 +1342,15 @@ function mapDuoToolRequest(toolName, args) {
|
|
|
1342
1342
|
if (program === TODO_WRITE_PROGRAM) {
|
|
1343
1343
|
return mapTodoWriteCall(args.arguments);
|
|
1344
1344
|
}
|
|
1345
|
+
if (program === WEBFETCH_PROGRAM) {
|
|
1346
|
+
return mapWebfetchCall(args.arguments);
|
|
1347
|
+
}
|
|
1348
|
+
if (program === QUESTION_PROGRAM) {
|
|
1349
|
+
return mapQuestionCall(args.arguments);
|
|
1350
|
+
}
|
|
1351
|
+
if (program === SKILL_PROGRAM) {
|
|
1352
|
+
return mapSkillCall(args.arguments);
|
|
1353
|
+
}
|
|
1345
1354
|
if (program) {
|
|
1346
1355
|
const parts = [shellQuote(program)];
|
|
1347
1356
|
if (Array.isArray(args.flags)) parts.push(...args.flags.map((f) => shellQuote(String(f))));
|
|
@@ -1390,28 +1399,76 @@ function mapDuoToolRequest(toolName, args) {
|
|
|
1390
1399
|
function asString2(value) {
|
|
1391
1400
|
return typeof value === "string" ? value : void 0;
|
|
1392
1401
|
}
|
|
1393
|
-
function
|
|
1402
|
+
function mapBridgeCommand(command) {
|
|
1394
1403
|
const normalized = command.trim();
|
|
1395
1404
|
if (normalized === TODO_READ_PROGRAM) {
|
|
1396
1405
|
return { toolName: "todoread", args: {} };
|
|
1397
1406
|
}
|
|
1407
|
+
if (normalized.startsWith(`${TODO_READ_PROGRAM} `)) {
|
|
1408
|
+
return invalidTool("todoread", `${TODO_READ_PROGRAM} does not accept a payload`);
|
|
1409
|
+
}
|
|
1398
1410
|
if (normalized === TODO_WRITE_PROGRAM) {
|
|
1399
1411
|
return invalidTool("todowrite", `${TODO_WRITE_PROGRAM} expects JSON payload after command prefix`);
|
|
1400
1412
|
}
|
|
1401
|
-
if (
|
|
1402
|
-
return
|
|
1413
|
+
if (normalized === WEBFETCH_PROGRAM) {
|
|
1414
|
+
return invalidTool("webfetch", `${WEBFETCH_PROGRAM} expects JSON payload after command prefix`);
|
|
1403
1415
|
}
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1416
|
+
if (normalized === QUESTION_PROGRAM) {
|
|
1417
|
+
return invalidTool("question", `${QUESTION_PROGRAM} expects JSON payload after command prefix`);
|
|
1418
|
+
}
|
|
1419
|
+
if (normalized === SKILL_PROGRAM) {
|
|
1420
|
+
return invalidTool("skill", `${SKILL_PROGRAM} expects JSON payload after command prefix`);
|
|
1407
1421
|
}
|
|
1408
|
-
|
|
1422
|
+
if (normalized.startsWith(`${TODO_WRITE_PROGRAM} `)) {
|
|
1423
|
+
const payload = normalized.slice(TODO_WRITE_PROGRAM.length).trim();
|
|
1424
|
+
if (!payload) {
|
|
1425
|
+
return invalidTool("todowrite", `${TODO_WRITE_PROGRAM} expects JSON payload after command prefix`);
|
|
1426
|
+
}
|
|
1427
|
+
return mapTodoWritePayload(payload);
|
|
1428
|
+
}
|
|
1429
|
+
if (normalized.startsWith(`${WEBFETCH_PROGRAM} `)) {
|
|
1430
|
+
const payload = normalized.slice(WEBFETCH_PROGRAM.length).trim();
|
|
1431
|
+
if (!payload) {
|
|
1432
|
+
return invalidTool("webfetch", `${WEBFETCH_PROGRAM} expects JSON payload after command prefix`);
|
|
1433
|
+
}
|
|
1434
|
+
return mapWebfetchPayload(payload);
|
|
1435
|
+
}
|
|
1436
|
+
if (normalized.startsWith(`${QUESTION_PROGRAM} `)) {
|
|
1437
|
+
const payload = normalized.slice(QUESTION_PROGRAM.length).trim();
|
|
1438
|
+
if (!payload) {
|
|
1439
|
+
return invalidTool("question", `${QUESTION_PROGRAM} expects JSON payload after command prefix`);
|
|
1440
|
+
}
|
|
1441
|
+
return mapQuestionPayload(payload);
|
|
1442
|
+
}
|
|
1443
|
+
if (normalized.startsWith(`${SKILL_PROGRAM} `)) {
|
|
1444
|
+
const payload = normalized.slice(SKILL_PROGRAM.length).trim();
|
|
1445
|
+
if (!payload) {
|
|
1446
|
+
return invalidTool("skill", `${SKILL_PROGRAM} expects JSON payload after command prefix`);
|
|
1447
|
+
}
|
|
1448
|
+
return mapSkillPayload(payload);
|
|
1449
|
+
}
|
|
1450
|
+
return null;
|
|
1409
1451
|
}
|
|
1410
1452
|
function mapTodoWriteCall(rawArguments) {
|
|
1411
|
-
const payloadResult =
|
|
1453
|
+
const payloadResult = parsePayloadFromArguments(rawArguments, TODO_WRITE_PROGRAM);
|
|
1412
1454
|
if ("error" in payloadResult) return invalidTool("todowrite", payloadResult.error);
|
|
1413
1455
|
return mapTodoWritePayload(payloadResult.payload);
|
|
1414
1456
|
}
|
|
1457
|
+
function mapWebfetchCall(rawArguments) {
|
|
1458
|
+
const payloadResult = parsePayloadFromArguments(rawArguments, WEBFETCH_PROGRAM);
|
|
1459
|
+
if ("error" in payloadResult) return invalidTool("webfetch", payloadResult.error);
|
|
1460
|
+
return mapWebfetchPayload(payloadResult.payload);
|
|
1461
|
+
}
|
|
1462
|
+
function mapQuestionCall(rawArguments) {
|
|
1463
|
+
const payloadResult = parsePayloadFromArguments(rawArguments, QUESTION_PROGRAM);
|
|
1464
|
+
if ("error" in payloadResult) return invalidTool("question", payloadResult.error);
|
|
1465
|
+
return mapQuestionPayload(payloadResult.payload);
|
|
1466
|
+
}
|
|
1467
|
+
function mapSkillCall(rawArguments) {
|
|
1468
|
+
const payloadResult = parsePayloadFromArguments(rawArguments, SKILL_PROGRAM);
|
|
1469
|
+
if ("error" in payloadResult) return invalidTool("skill", payloadResult.error);
|
|
1470
|
+
return mapSkillPayload(payloadResult.payload);
|
|
1471
|
+
}
|
|
1415
1472
|
function mapTodoWritePayload(rawPayload) {
|
|
1416
1473
|
const payloadResult = parseTodoPayload(rawPayload);
|
|
1417
1474
|
if ("error" in payloadResult) return invalidTool("todowrite", payloadResult.error);
|
|
@@ -1424,35 +1481,181 @@ function mapTodoWritePayload(rawPayload) {
|
|
|
1424
1481
|
}
|
|
1425
1482
|
};
|
|
1426
1483
|
}
|
|
1427
|
-
function
|
|
1484
|
+
function mapWebfetchPayload(rawPayload) {
|
|
1485
|
+
const payloadResult = parseWebfetchPayload(rawPayload);
|
|
1486
|
+
if ("error" in payloadResult) return invalidTool("webfetch", payloadResult.error);
|
|
1487
|
+
return {
|
|
1488
|
+
toolName: "webfetch",
|
|
1489
|
+
args: payloadResult.args
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1492
|
+
function mapQuestionPayload(rawPayload) {
|
|
1493
|
+
const payloadResult = parseQuestionPayload(rawPayload);
|
|
1494
|
+
if ("error" in payloadResult) return invalidTool("question", payloadResult.error);
|
|
1495
|
+
return {
|
|
1496
|
+
toolName: "question",
|
|
1497
|
+
args: payloadResult.args
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
function mapSkillPayload(rawPayload) {
|
|
1501
|
+
const payloadResult = parseSkillPayload(rawPayload);
|
|
1502
|
+
if ("error" in payloadResult) return invalidTool("skill", payloadResult.error);
|
|
1503
|
+
return {
|
|
1504
|
+
toolName: "skill",
|
|
1505
|
+
args: payloadResult.args
|
|
1506
|
+
};
|
|
1507
|
+
}
|
|
1508
|
+
function parsePayloadFromArguments(rawArguments, program) {
|
|
1428
1509
|
if (!Array.isArray(rawArguments) || rawArguments.length === 0) {
|
|
1429
1510
|
return {
|
|
1430
|
-
error: `${
|
|
1511
|
+
error: `${program} expects JSON payload in arguments[0]`
|
|
1431
1512
|
};
|
|
1432
1513
|
}
|
|
1433
1514
|
const rawPayload = rawArguments[0];
|
|
1434
1515
|
if (typeof rawPayload !== "string") {
|
|
1435
1516
|
return {
|
|
1436
|
-
error: `${
|
|
1517
|
+
error: `${program} expects arguments[0] to be a JSON string`
|
|
1437
1518
|
};
|
|
1438
1519
|
}
|
|
1439
1520
|
return { payload: rawPayload };
|
|
1440
1521
|
}
|
|
1441
1522
|
function parseTodoPayload(rawPayload) {
|
|
1523
|
+
return parseObjectPayload(rawPayload, TODO_WRITE_PROGRAM);
|
|
1524
|
+
}
|
|
1525
|
+
function parseObjectPayload(rawPayload, program) {
|
|
1442
1526
|
const normalized = unwrapWrappingQuotes(rawPayload);
|
|
1443
1527
|
try {
|
|
1444
1528
|
const parsed = JSON.parse(normalized);
|
|
1445
1529
|
if (!isRecord(parsed)) {
|
|
1446
1530
|
return {
|
|
1447
|
-
error: `${
|
|
1531
|
+
error: `${program} payload must be a JSON object`
|
|
1448
1532
|
};
|
|
1449
1533
|
}
|
|
1450
1534
|
return { payload: parsed };
|
|
1451
1535
|
} catch {
|
|
1452
1536
|
return {
|
|
1453
|
-
error: `${
|
|
1537
|
+
error: `${program} payload is not valid JSON`
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
function parseWebfetchPayload(rawPayload) {
|
|
1542
|
+
const payloadResult = parseObjectPayload(rawPayload, WEBFETCH_PROGRAM);
|
|
1543
|
+
if ("error" in payloadResult) return payloadResult;
|
|
1544
|
+
const url = asString2(payloadResult.payload.url);
|
|
1545
|
+
if (!url) {
|
|
1546
|
+
return {
|
|
1547
|
+
error: `${WEBFETCH_PROGRAM} payload must include a url string`
|
|
1454
1548
|
};
|
|
1455
1549
|
}
|
|
1550
|
+
const args = { url };
|
|
1551
|
+
const format = asString2(payloadResult.payload.format);
|
|
1552
|
+
if (format !== void 0) {
|
|
1553
|
+
if (!WEBFETCH_FORMATS.has(format)) {
|
|
1554
|
+
return {
|
|
1555
|
+
error: `${WEBFETCH_PROGRAM} format must be one of: text, markdown, html`
|
|
1556
|
+
};
|
|
1557
|
+
}
|
|
1558
|
+
args.format = format;
|
|
1559
|
+
}
|
|
1560
|
+
const timeout = payloadResult.payload.timeout;
|
|
1561
|
+
if (timeout !== void 0) {
|
|
1562
|
+
if (typeof timeout !== "number" || !Number.isFinite(timeout) || timeout <= 0) {
|
|
1563
|
+
return {
|
|
1564
|
+
error: `${WEBFETCH_PROGRAM} timeout must be a positive number`
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
1567
|
+
args.timeout = timeout;
|
|
1568
|
+
}
|
|
1569
|
+
return { args };
|
|
1570
|
+
}
|
|
1571
|
+
function parseQuestionPayload(rawPayload) {
|
|
1572
|
+
const payloadResult = parseObjectPayload(rawPayload, QUESTION_PROGRAM);
|
|
1573
|
+
if ("error" in payloadResult) return payloadResult;
|
|
1574
|
+
const rawQuestions = payloadResult.payload.questions;
|
|
1575
|
+
if (!Array.isArray(rawQuestions) || rawQuestions.length === 0) {
|
|
1576
|
+
return {
|
|
1577
|
+
error: `${QUESTION_PROGRAM} payload must include a non-empty questions array`
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
const questions = [];
|
|
1581
|
+
for (let i = 0; i < rawQuestions.length; i += 1) {
|
|
1582
|
+
const rawQuestion = rawQuestions[i];
|
|
1583
|
+
if (!isRecord(rawQuestion)) {
|
|
1584
|
+
return {
|
|
1585
|
+
error: `${QUESTION_PROGRAM} questions[${i}] must be an object`
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
const question = asString2(rawQuestion.question);
|
|
1589
|
+
if (!question) {
|
|
1590
|
+
return {
|
|
1591
|
+
error: `${QUESTION_PROGRAM} questions[${i}].question must be a string`
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
const header = asString2(rawQuestion.header);
|
|
1595
|
+
if (!header) {
|
|
1596
|
+
return {
|
|
1597
|
+
error: `${QUESTION_PROGRAM} questions[${i}].header must be a string`
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
const rawOptions = rawQuestion.options;
|
|
1601
|
+
if (!Array.isArray(rawOptions) || rawOptions.length === 0) {
|
|
1602
|
+
return {
|
|
1603
|
+
error: `${QUESTION_PROGRAM} questions[${i}].options must be a non-empty array`
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
const options = [];
|
|
1607
|
+
for (let j = 0; j < rawOptions.length; j += 1) {
|
|
1608
|
+
const rawOption = rawOptions[j];
|
|
1609
|
+
if (!isRecord(rawOption)) {
|
|
1610
|
+
return {
|
|
1611
|
+
error: `${QUESTION_PROGRAM} questions[${i}].options[${j}] must be an object`
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1614
|
+
const label = asString2(rawOption.label);
|
|
1615
|
+
if (!label) {
|
|
1616
|
+
return {
|
|
1617
|
+
error: `${QUESTION_PROGRAM} questions[${i}].options[${j}].label must be a string`
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
const description = asString2(rawOption.description);
|
|
1621
|
+
if (!description) {
|
|
1622
|
+
return {
|
|
1623
|
+
error: `${QUESTION_PROGRAM} questions[${i}].options[${j}].description must be a string`
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
options.push({ label, description });
|
|
1627
|
+
}
|
|
1628
|
+
const mappedQuestion = { question, header, options };
|
|
1629
|
+
if (rawQuestion.multiple !== void 0) {
|
|
1630
|
+
if (typeof rawQuestion.multiple !== "boolean") {
|
|
1631
|
+
return {
|
|
1632
|
+
error: `${QUESTION_PROGRAM} questions[${i}].multiple must be a boolean`
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
mappedQuestion.multiple = rawQuestion.multiple;
|
|
1636
|
+
}
|
|
1637
|
+
questions.push(mappedQuestion);
|
|
1638
|
+
}
|
|
1639
|
+
return {
|
|
1640
|
+
args: {
|
|
1641
|
+
questions
|
|
1642
|
+
}
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
function parseSkillPayload(rawPayload) {
|
|
1646
|
+
const payloadResult = parseObjectPayload(rawPayload, SKILL_PROGRAM);
|
|
1647
|
+
if ("error" in payloadResult) return payloadResult;
|
|
1648
|
+
const name = asString2(payloadResult.payload.name)?.trim();
|
|
1649
|
+
if (!name) {
|
|
1650
|
+
return {
|
|
1651
|
+
error: `${SKILL_PROGRAM} payload must include a name string`
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
return {
|
|
1655
|
+
args: {
|
|
1656
|
+
name
|
|
1657
|
+
}
|
|
1658
|
+
};
|
|
1456
1659
|
}
|
|
1457
1660
|
function unwrapWrappingQuotes(value) {
|
|
1458
1661
|
const trimmed = value.trim();
|
|
@@ -1671,13 +1874,25 @@ var TOOL_ALLOWLIST = [
|
|
|
1671
1874
|
"run_command"
|
|
1672
1875
|
];
|
|
1673
1876
|
var TODO_BRIDGE_INSTRUCTIONS = [
|
|
1674
|
-
"Todo
|
|
1675
|
-
"- For
|
|
1877
|
+
"Todo planning and execution (required):",
|
|
1878
|
+
"- For complex or multi-step work (3+ steps), create and maintain a todo list.",
|
|
1879
|
+
"- Keep exactly one task as in_progress at a time.",
|
|
1880
|
+
"- Mark tasks completed immediately after finishing them.",
|
|
1881
|
+
"- Update the todo list whenever scope changes or new requirements appear.",
|
|
1882
|
+
"- Skip todo planning for trivial one-step requests.",
|
|
1883
|
+
"- To read the current todo list, call run_command with command: __todo_read__",
|
|
1884
|
+
"- To create or update the todo list, call run_command with command:",
|
|
1676
1885
|
' __todo_write__ {"todos":[{"content":"...","status":"pending|in_progress|completed|cancelled","priority":"high|medium|low"}]}',
|
|
1677
|
-
"-
|
|
1678
|
-
"
|
|
1679
|
-
"-
|
|
1680
|
-
"
|
|
1886
|
+
"- To fetch web content, call run_command with command:",
|
|
1887
|
+
' __webfetch__ {"url":"https://example.com","format":"markdown","timeout":30}',
|
|
1888
|
+
"- To ask the user questions, call run_command with command:",
|
|
1889
|
+
' __question__ {"questions":[{"question":"...","header":"...","options":[{"label":"Option A","description":"..."}],"multiple":false}]}',
|
|
1890
|
+
"- To load a skill, call run_command with command:",
|
|
1891
|
+
' __skill__ {"name":"skill-name"}',
|
|
1892
|
+
"- Use strict JSON with double quotes in all bridge payloads.",
|
|
1893
|
+
"- For __question__, custom is enabled by default; do not add generic Other options.",
|
|
1894
|
+
"- If payload validation fails, correct the JSON and retry.",
|
|
1895
|
+
"- For todo, webfetch, question, and skill operations, only use __todo_read__, __todo_write__, __webfetch__, __question__, and __skill__."
|
|
1681
1896
|
].join("\n");
|
|
1682
1897
|
function buildFlowConfig(systemPrompt) {
|
|
1683
1898
|
const bridgedPrompt = [systemPrompt.trim(), TODO_BRIDGE_INSTRUCTIONS].filter(Boolean).join("\n\n");
|