opencode-gitlab-duo-agentic-custom-tools 0.3.3 → 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 +240 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1264,8 +1264,12 @@ function isPlainObject(value) {
|
|
|
1264
1264
|
// src/provider/tool-mapping.ts
|
|
1265
1265
|
var TODO_WRITE_PROGRAM = "__todo_write__";
|
|
1266
1266
|
var TODO_READ_PROGRAM = "__todo_read__";
|
|
1267
|
+
var WEBFETCH_PROGRAM = "__webfetch__";
|
|
1268
|
+
var QUESTION_PROGRAM = "__question__";
|
|
1269
|
+
var SKILL_PROGRAM = "__skill__";
|
|
1267
1270
|
var TODO_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress", "completed", "cancelled"]);
|
|
1268
1271
|
var TODO_PRIORITIES = /* @__PURE__ */ new Set(["high", "medium", "low"]);
|
|
1272
|
+
var WEBFETCH_FORMATS = /* @__PURE__ */ new Set(["text", "markdown", "html"]);
|
|
1269
1273
|
function mapDuoToolRequest(toolName, args) {
|
|
1270
1274
|
switch (toolName) {
|
|
1271
1275
|
case "list_dir": {
|
|
@@ -1321,14 +1325,14 @@ function mapDuoToolRequest(toolName, args) {
|
|
|
1321
1325
|
case "shell_command": {
|
|
1322
1326
|
const command = asString2(args.command);
|
|
1323
1327
|
if (!command) return { toolName, args };
|
|
1324
|
-
const bridged =
|
|
1328
|
+
const bridged = mapBridgeCommand(command);
|
|
1325
1329
|
if (bridged) return bridged;
|
|
1326
1330
|
return { toolName: "bash", args: { command, description: "Run shell command", workdir: "." } };
|
|
1327
1331
|
}
|
|
1328
1332
|
case "run_command": {
|
|
1329
1333
|
const command = asString2(args.command);
|
|
1330
1334
|
if (command) {
|
|
1331
|
-
const bridged =
|
|
1335
|
+
const bridged = mapBridgeCommand(command);
|
|
1332
1336
|
if (bridged) return bridged;
|
|
1333
1337
|
}
|
|
1334
1338
|
const program = asString2(args.program);
|
|
@@ -1338,6 +1342,15 @@ function mapDuoToolRequest(toolName, args) {
|
|
|
1338
1342
|
if (program === TODO_WRITE_PROGRAM) {
|
|
1339
1343
|
return mapTodoWriteCall(args.arguments);
|
|
1340
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
|
+
}
|
|
1341
1354
|
if (program) {
|
|
1342
1355
|
const parts = [shellQuote(program)];
|
|
1343
1356
|
if (Array.isArray(args.flags)) parts.push(...args.flags.map((f) => shellQuote(String(f))));
|
|
@@ -1386,28 +1399,76 @@ function mapDuoToolRequest(toolName, args) {
|
|
|
1386
1399
|
function asString2(value) {
|
|
1387
1400
|
return typeof value === "string" ? value : void 0;
|
|
1388
1401
|
}
|
|
1389
|
-
function
|
|
1402
|
+
function mapBridgeCommand(command) {
|
|
1390
1403
|
const normalized = command.trim();
|
|
1391
1404
|
if (normalized === TODO_READ_PROGRAM) {
|
|
1392
1405
|
return { toolName: "todoread", args: {} };
|
|
1393
1406
|
}
|
|
1407
|
+
if (normalized.startsWith(`${TODO_READ_PROGRAM} `)) {
|
|
1408
|
+
return invalidTool("todoread", `${TODO_READ_PROGRAM} does not accept a payload`);
|
|
1409
|
+
}
|
|
1394
1410
|
if (normalized === TODO_WRITE_PROGRAM) {
|
|
1395
1411
|
return invalidTool("todowrite", `${TODO_WRITE_PROGRAM} expects JSON payload after command prefix`);
|
|
1396
1412
|
}
|
|
1397
|
-
if (
|
|
1398
|
-
return
|
|
1413
|
+
if (normalized === WEBFETCH_PROGRAM) {
|
|
1414
|
+
return invalidTool("webfetch", `${WEBFETCH_PROGRAM} expects JSON payload after command prefix`);
|
|
1399
1415
|
}
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
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`);
|
|
1403
1421
|
}
|
|
1404
|
-
|
|
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;
|
|
1405
1451
|
}
|
|
1406
1452
|
function mapTodoWriteCall(rawArguments) {
|
|
1407
|
-
const payloadResult =
|
|
1453
|
+
const payloadResult = parsePayloadFromArguments(rawArguments, TODO_WRITE_PROGRAM);
|
|
1408
1454
|
if ("error" in payloadResult) return invalidTool("todowrite", payloadResult.error);
|
|
1409
1455
|
return mapTodoWritePayload(payloadResult.payload);
|
|
1410
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
|
+
}
|
|
1411
1472
|
function mapTodoWritePayload(rawPayload) {
|
|
1412
1473
|
const payloadResult = parseTodoPayload(rawPayload);
|
|
1413
1474
|
if ("error" in payloadResult) return invalidTool("todowrite", payloadResult.error);
|
|
@@ -1420,35 +1481,181 @@ function mapTodoWritePayload(rawPayload) {
|
|
|
1420
1481
|
}
|
|
1421
1482
|
};
|
|
1422
1483
|
}
|
|
1423
|
-
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) {
|
|
1424
1509
|
if (!Array.isArray(rawArguments) || rawArguments.length === 0) {
|
|
1425
1510
|
return {
|
|
1426
|
-
error: `${
|
|
1511
|
+
error: `${program} expects JSON payload in arguments[0]`
|
|
1427
1512
|
};
|
|
1428
1513
|
}
|
|
1429
1514
|
const rawPayload = rawArguments[0];
|
|
1430
1515
|
if (typeof rawPayload !== "string") {
|
|
1431
1516
|
return {
|
|
1432
|
-
error: `${
|
|
1517
|
+
error: `${program} expects arguments[0] to be a JSON string`
|
|
1433
1518
|
};
|
|
1434
1519
|
}
|
|
1435
1520
|
return { payload: rawPayload };
|
|
1436
1521
|
}
|
|
1437
1522
|
function parseTodoPayload(rawPayload) {
|
|
1523
|
+
return parseObjectPayload(rawPayload, TODO_WRITE_PROGRAM);
|
|
1524
|
+
}
|
|
1525
|
+
function parseObjectPayload(rawPayload, program) {
|
|
1438
1526
|
const normalized = unwrapWrappingQuotes(rawPayload);
|
|
1439
1527
|
try {
|
|
1440
1528
|
const parsed = JSON.parse(normalized);
|
|
1441
1529
|
if (!isRecord(parsed)) {
|
|
1442
1530
|
return {
|
|
1443
|
-
error: `${
|
|
1531
|
+
error: `${program} payload must be a JSON object`
|
|
1444
1532
|
};
|
|
1445
1533
|
}
|
|
1446
1534
|
return { payload: parsed };
|
|
1447
1535
|
} catch {
|
|
1448
1536
|
return {
|
|
1449
|
-
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`
|
|
1548
|
+
};
|
|
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`
|
|
1450
1652
|
};
|
|
1451
1653
|
}
|
|
1654
|
+
return {
|
|
1655
|
+
args: {
|
|
1656
|
+
name
|
|
1657
|
+
}
|
|
1658
|
+
};
|
|
1452
1659
|
}
|
|
1453
1660
|
function unwrapWrappingQuotes(value) {
|
|
1454
1661
|
const trimmed = value.trim();
|
|
@@ -1667,13 +1874,25 @@ var TOOL_ALLOWLIST = [
|
|
|
1667
1874
|
"run_command"
|
|
1668
1875
|
];
|
|
1669
1876
|
var TODO_BRIDGE_INSTRUCTIONS = [
|
|
1670
|
-
"Todo
|
|
1671
|
-
"- 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:",
|
|
1672
1885
|
' __todo_write__ {"todos":[{"content":"...","status":"pending|in_progress|completed|cancelled","priority":"high|medium|low"}]}',
|
|
1673
|
-
"-
|
|
1674
|
-
"
|
|
1675
|
-
"-
|
|
1676
|
-
"
|
|
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__."
|
|
1677
1896
|
].join("\n");
|
|
1678
1897
|
function buildFlowConfig(systemPrompt) {
|
|
1679
1898
|
const bridgedPrompt = [systemPrompt.trim(), TODO_BRIDGE_INSTRUCTIONS].filter(Boolean).join("\n\n");
|