opencode-gitlab-duo-agentic 0.2.5 → 0.2.6
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 +248 -27
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1089,7 +1089,7 @@ var WorkflowSession = class {
|
|
|
1089
1089
|
// ---------------------------------------------------------------------------
|
|
1090
1090
|
// Messaging
|
|
1091
1091
|
// ---------------------------------------------------------------------------
|
|
1092
|
-
sendStartRequest(goal) {
|
|
1092
|
+
sendStartRequest(goal, additionalContext = []) {
|
|
1093
1093
|
if (!this.#socket || !this.#workflowId) throw new Error("Not connected");
|
|
1094
1094
|
const mcpTools = this.#toolsConfig?.mcpTools ?? [];
|
|
1095
1095
|
const preapprovedTools = mcpTools.map((t) => t.name);
|
|
@@ -1104,7 +1104,7 @@ var WorkflowSession = class {
|
|
|
1104
1104
|
}),
|
|
1105
1105
|
clientCapabilities: ["shell_command"],
|
|
1106
1106
|
mcpTools,
|
|
1107
|
-
additional_context:
|
|
1107
|
+
additional_context: additionalContext,
|
|
1108
1108
|
preapproved_tools: preapprovedTools,
|
|
1109
1109
|
...this.#toolsConfig?.flowConfig ? {
|
|
1110
1110
|
flowConfig: this.#toolsConfig.flowConfig,
|
|
@@ -1265,16 +1265,28 @@ function extractToolResults(prompt) {
|
|
|
1265
1265
|
if (!Array.isArray(content)) continue;
|
|
1266
1266
|
for (const part of content) {
|
|
1267
1267
|
const p = part;
|
|
1268
|
-
if (p.type
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1268
|
+
if (p.type === "tool-result") {
|
|
1269
|
+
const toolCallId = String(p.toolCallId ?? "");
|
|
1270
|
+
const toolName = String(p.toolName ?? "");
|
|
1271
|
+
if (!toolCallId) continue;
|
|
1272
|
+
const { output, error } = parseToolResultOutput(p);
|
|
1273
|
+
const finalError = error ?? asString2(p.error) ?? asString2(p.errorText);
|
|
1274
|
+
results.push({ toolCallId, toolName, output, error: finalError });
|
|
1275
|
+
}
|
|
1276
|
+
if (p.type === "tool-error") {
|
|
1277
|
+
const toolCallId = String(p.toolCallId ?? "");
|
|
1278
|
+
const toolName = String(p.toolName ?? "");
|
|
1279
|
+
const errorValue = p.error ?? p.errorText ?? p.message;
|
|
1280
|
+
const error = asString2(errorValue) ?? String(errorValue ?? "");
|
|
1281
|
+
results.push({ toolCallId, toolName, output: "", error });
|
|
1282
|
+
}
|
|
1274
1283
|
}
|
|
1275
1284
|
}
|
|
1276
1285
|
return results;
|
|
1277
1286
|
}
|
|
1287
|
+
function asString2(value) {
|
|
1288
|
+
return typeof value === "string" ? value : void 0;
|
|
1289
|
+
}
|
|
1278
1290
|
function parseToolResultOutput(part) {
|
|
1279
1291
|
const outputField = part.output;
|
|
1280
1292
|
const resultField = part.result;
|
|
@@ -1296,10 +1308,67 @@ function parseToolResultOutput(part) {
|
|
|
1296
1308
|
return { output: typeof outputField === "string" ? outputField : JSON.stringify(outputField) };
|
|
1297
1309
|
}
|
|
1298
1310
|
if (resultField !== void 0) {
|
|
1299
|
-
|
|
1311
|
+
const output = typeof resultField === "string" ? resultField : JSON.stringify(resultField);
|
|
1312
|
+
const error = isPlainObject(resultField) ? asString2(resultField.error) : void 0;
|
|
1313
|
+
return { output, error };
|
|
1300
1314
|
}
|
|
1301
1315
|
return { output: "" };
|
|
1302
1316
|
}
|
|
1317
|
+
function extractSystemPrompt(prompt) {
|
|
1318
|
+
if (!Array.isArray(prompt)) return null;
|
|
1319
|
+
const parts = [];
|
|
1320
|
+
for (const message of prompt) {
|
|
1321
|
+
const msg = message;
|
|
1322
|
+
if (msg.role === "system" && typeof msg.content === "string" && msg.content.trim()) {
|
|
1323
|
+
parts.push(msg.content);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
return parts.length > 0 ? parts.join("\n") : null;
|
|
1327
|
+
}
|
|
1328
|
+
function sanitizeSystemPrompt(prompt) {
|
|
1329
|
+
let result = prompt;
|
|
1330
|
+
result = result.replace(/^You are [Oo]pen[Cc]ode[,.].*$/gm, "");
|
|
1331
|
+
result = result.replace(/^Your name is opencode\s*$/gm, "");
|
|
1332
|
+
result = result.replace(
|
|
1333
|
+
/If the user asks for help or wants to give feedback[\s\S]*?https:\/\/github\.com\/anomalyco\/opencode\s*/g,
|
|
1334
|
+
""
|
|
1335
|
+
);
|
|
1336
|
+
result = result.replace(
|
|
1337
|
+
/When the user directly asks about OpenCode[\s\S]*?https:\/\/opencode\.ai\/docs\s*/g,
|
|
1338
|
+
""
|
|
1339
|
+
);
|
|
1340
|
+
result = result.replace(/https:\/\/github\.com\/anomalyco\/opencode\S*/g, "");
|
|
1341
|
+
result = result.replace(/https:\/\/opencode\.ai\S*/g, "");
|
|
1342
|
+
result = result.replace(/\bOpenCode\b/g, "GitLab Duo");
|
|
1343
|
+
result = result.replace(/\bopencode\b/g, "GitLab Duo");
|
|
1344
|
+
result = result.replace(/The exact model ID is GitLab Duo\//g, "The exact model ID is ");
|
|
1345
|
+
result = result.replace(/\n{3,}/g, "\n\n");
|
|
1346
|
+
return result.trim();
|
|
1347
|
+
}
|
|
1348
|
+
function extractAgentReminders(prompt) {
|
|
1349
|
+
if (!Array.isArray(prompt)) return [];
|
|
1350
|
+
let textParts = [];
|
|
1351
|
+
for (let i = prompt.length - 1; i >= 0; i--) {
|
|
1352
|
+
const message = prompt[i];
|
|
1353
|
+
if (message?.role !== "user" || !Array.isArray(message.content)) continue;
|
|
1354
|
+
textParts = message.content.filter((p) => p.type === "text");
|
|
1355
|
+
if (textParts.length > 0) break;
|
|
1356
|
+
}
|
|
1357
|
+
if (textParts.length === 0) return [];
|
|
1358
|
+
const reminders = [];
|
|
1359
|
+
for (const part of textParts) {
|
|
1360
|
+
if (!part.text) continue;
|
|
1361
|
+
const text2 = String(part.text);
|
|
1362
|
+
if (part.synthetic) {
|
|
1363
|
+
const trimmed = text2.trim();
|
|
1364
|
+
if (trimmed.length > 0) reminders.push(trimmed);
|
|
1365
|
+
continue;
|
|
1366
|
+
}
|
|
1367
|
+
const matches = text2.match(/<system-reminder>[\s\S]*?<\/system-reminder>/g);
|
|
1368
|
+
if (matches) reminders.push(...matches);
|
|
1369
|
+
}
|
|
1370
|
+
return reminders;
|
|
1371
|
+
}
|
|
1303
1372
|
function isPlainObject(value) {
|
|
1304
1373
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1305
1374
|
}
|
|
@@ -1308,14 +1377,14 @@ function isPlainObject(value) {
|
|
|
1308
1377
|
function mapDuoToolRequest(toolName, args) {
|
|
1309
1378
|
switch (toolName) {
|
|
1310
1379
|
case "list_dir": {
|
|
1311
|
-
const directory =
|
|
1380
|
+
const directory = asString3(args.directory) ?? ".";
|
|
1312
1381
|
return {
|
|
1313
1382
|
toolName: "bash",
|
|
1314
1383
|
args: { command: `ls -la ${shellQuote(directory)}`, description: "List directory contents", workdir: "." }
|
|
1315
1384
|
};
|
|
1316
1385
|
}
|
|
1317
1386
|
case "read_file": {
|
|
1318
|
-
const filePath =
|
|
1387
|
+
const filePath = asString3(args.file_path) ?? asString3(args.filepath) ?? asString3(args.filePath) ?? asString3(args.path);
|
|
1319
1388
|
if (!filePath) return { toolName, args };
|
|
1320
1389
|
const mapped = { filePath };
|
|
1321
1390
|
if (typeof args.offset === "number") mapped.offset = args.offset;
|
|
@@ -1328,27 +1397,27 @@ function mapDuoToolRequest(toolName, args) {
|
|
|
1328
1397
|
return filePaths.map((fp) => ({ toolName: "read", args: { filePath: fp } }));
|
|
1329
1398
|
}
|
|
1330
1399
|
case "create_file_with_contents": {
|
|
1331
|
-
const filePath =
|
|
1332
|
-
const content =
|
|
1400
|
+
const filePath = asString3(args.file_path);
|
|
1401
|
+
const content = asString3(args.contents);
|
|
1333
1402
|
if (!filePath || content === void 0) return { toolName, args };
|
|
1334
1403
|
return { toolName: "write", args: { filePath, content } };
|
|
1335
1404
|
}
|
|
1336
1405
|
case "edit_file": {
|
|
1337
|
-
const filePath =
|
|
1338
|
-
const oldString =
|
|
1339
|
-
const newString =
|
|
1406
|
+
const filePath = asString3(args.file_path);
|
|
1407
|
+
const oldString = asString3(args.old_str);
|
|
1408
|
+
const newString = asString3(args.new_str);
|
|
1340
1409
|
if (!filePath || oldString === void 0 || newString === void 0) return { toolName, args };
|
|
1341
1410
|
return { toolName: "edit", args: { filePath, oldString, newString } };
|
|
1342
1411
|
}
|
|
1343
1412
|
case "find_files": {
|
|
1344
|
-
const pattern =
|
|
1413
|
+
const pattern = asString3(args.name_pattern);
|
|
1345
1414
|
if (!pattern) return { toolName, args };
|
|
1346
1415
|
return { toolName: "glob", args: { pattern } };
|
|
1347
1416
|
}
|
|
1348
1417
|
case "grep": {
|
|
1349
|
-
const pattern =
|
|
1418
|
+
const pattern = asString3(args.pattern);
|
|
1350
1419
|
if (!pattern) return { toolName, args };
|
|
1351
|
-
const searchDir =
|
|
1420
|
+
const searchDir = asString3(args.search_directory);
|
|
1352
1421
|
const caseInsensitive = Boolean(args.case_insensitive);
|
|
1353
1422
|
const normalizedPattern = caseInsensitive && !pattern.startsWith("(?i)") ? `(?i)${pattern}` : pattern;
|
|
1354
1423
|
const mapped = { pattern: normalizedPattern };
|
|
@@ -1356,32 +1425,32 @@ function mapDuoToolRequest(toolName, args) {
|
|
|
1356
1425
|
return { toolName: "grep", args: mapped };
|
|
1357
1426
|
}
|
|
1358
1427
|
case "mkdir": {
|
|
1359
|
-
const directory =
|
|
1428
|
+
const directory = asString3(args.directory_path);
|
|
1360
1429
|
if (!directory) return { toolName, args };
|
|
1361
1430
|
return { toolName: "bash", args: { command: `mkdir -p ${shellQuote(directory)}`, description: "Create directory", workdir: "." } };
|
|
1362
1431
|
}
|
|
1363
1432
|
case "shell_command": {
|
|
1364
|
-
const command =
|
|
1433
|
+
const command = asString3(args.command);
|
|
1365
1434
|
if (!command) return { toolName, args };
|
|
1366
1435
|
return { toolName: "bash", args: { command, description: "Run shell command", workdir: "." } };
|
|
1367
1436
|
}
|
|
1368
1437
|
case "run_command": {
|
|
1369
|
-
const program =
|
|
1438
|
+
const program = asString3(args.program);
|
|
1370
1439
|
if (program) {
|
|
1371
1440
|
const parts = [shellQuote(program)];
|
|
1372
1441
|
if (Array.isArray(args.flags)) parts.push(...args.flags.map((f) => shellQuote(String(f))));
|
|
1373
1442
|
if (Array.isArray(args.arguments)) parts.push(...args.arguments.map((a) => shellQuote(String(a))));
|
|
1374
1443
|
return { toolName: "bash", args: { command: parts.join(" "), description: "Run command", workdir: "." } };
|
|
1375
1444
|
}
|
|
1376
|
-
const command =
|
|
1445
|
+
const command = asString3(args.command);
|
|
1377
1446
|
if (!command) return { toolName, args };
|
|
1378
1447
|
return { toolName: "bash", args: { command, description: "Run command", workdir: "." } };
|
|
1379
1448
|
}
|
|
1380
1449
|
case "run_git_command": {
|
|
1381
|
-
const command =
|
|
1450
|
+
const command = asString3(args.command);
|
|
1382
1451
|
if (!command) return { toolName, args };
|
|
1383
1452
|
const rawArgs = args.args;
|
|
1384
|
-
const extraArgs = Array.isArray(rawArgs) ? rawArgs.map((v) => shellQuote(String(v))).join(" ") :
|
|
1453
|
+
const extraArgs = Array.isArray(rawArgs) ? rawArgs.map((v) => shellQuote(String(v))).join(" ") : asString3(rawArgs);
|
|
1385
1454
|
const gitCmd = extraArgs ? `git ${shellQuote(command)} ${extraArgs}` : `git ${shellQuote(command)}`;
|
|
1386
1455
|
return { toolName: "bash", args: { command: gitCmd, description: "Run git command", workdir: "." } };
|
|
1387
1456
|
}
|
|
@@ -1389,7 +1458,7 @@ function mapDuoToolRequest(toolName, args) {
|
|
|
1389
1458
|
return { toolName, args };
|
|
1390
1459
|
}
|
|
1391
1460
|
}
|
|
1392
|
-
function
|
|
1461
|
+
function asString3(value) {
|
|
1393
1462
|
return typeof value === "string" ? value : void 0;
|
|
1394
1463
|
}
|
|
1395
1464
|
function asStringArray2(value) {
|
|
@@ -1421,6 +1490,85 @@ function readProviderBlock(options) {
|
|
|
1421
1490
|
return void 0;
|
|
1422
1491
|
}
|
|
1423
1492
|
|
|
1493
|
+
// src/provider/system-context.ts
|
|
1494
|
+
import os2 from "os";
|
|
1495
|
+
function buildSystemContext() {
|
|
1496
|
+
const platform = os2.platform();
|
|
1497
|
+
const arch = os2.arch();
|
|
1498
|
+
return [
|
|
1499
|
+
{
|
|
1500
|
+
category: "os_information",
|
|
1501
|
+
content: `<os><platform>${platform}</platform><architecture>${arch}</architecture></os>`,
|
|
1502
|
+
id: "os_information",
|
|
1503
|
+
metadata: JSON.stringify({
|
|
1504
|
+
title: "Operating System",
|
|
1505
|
+
enabled: true,
|
|
1506
|
+
subType: "os"
|
|
1507
|
+
})
|
|
1508
|
+
},
|
|
1509
|
+
{
|
|
1510
|
+
category: "user_rule",
|
|
1511
|
+
content: SYSTEM_RULES,
|
|
1512
|
+
id: "user_rules",
|
|
1513
|
+
metadata: JSON.stringify({
|
|
1514
|
+
title: "System Rules",
|
|
1515
|
+
enabled: true,
|
|
1516
|
+
subType: "user_rule"
|
|
1517
|
+
})
|
|
1518
|
+
}
|
|
1519
|
+
];
|
|
1520
|
+
}
|
|
1521
|
+
var SYSTEM_RULES = `<system-reminder>
|
|
1522
|
+
You MUST follow ALL the rules in this block strictly.
|
|
1523
|
+
|
|
1524
|
+
<tool_orchestration>
|
|
1525
|
+
PARALLEL EXECUTION:
|
|
1526
|
+
- When gathering information, plan all needed searches upfront and execute
|
|
1527
|
+
them together using multiple tool calls in the same turn where possible.
|
|
1528
|
+
- Read multiple related files together rather than one at a time.
|
|
1529
|
+
- Patterns: grep + find_files together, read_file for multiple files together.
|
|
1530
|
+
|
|
1531
|
+
SEQUENTIAL EXECUTION (only when output depends on previous step):
|
|
1532
|
+
- Read a file BEFORE editing it (always).
|
|
1533
|
+
- Check dependencies BEFORE importing them.
|
|
1534
|
+
- Run tests AFTER making changes.
|
|
1535
|
+
|
|
1536
|
+
READ BEFORE WRITE:
|
|
1537
|
+
- Always read existing files before modifying them to understand context.
|
|
1538
|
+
- Check for existing patterns (naming, imports, error handling) and match them.
|
|
1539
|
+
- Verify the exact content to replace when using edit_file.
|
|
1540
|
+
|
|
1541
|
+
ERROR HANDLING:
|
|
1542
|
+
- If a tool fails, analyze the error before retrying.
|
|
1543
|
+
- If a shell command fails, check the error output and adapt.
|
|
1544
|
+
- Do not repeat the same failing operation without changes.
|
|
1545
|
+
</tool_orchestration>
|
|
1546
|
+
|
|
1547
|
+
<development_workflow>
|
|
1548
|
+
For software development tasks, follow this workflow:
|
|
1549
|
+
|
|
1550
|
+
1. UNDERSTAND: Read relevant files, explore the codebase structure
|
|
1551
|
+
2. PLAN: Break down the task into clear steps
|
|
1552
|
+
3. IMPLEMENT: Make changes methodically, one step at a time
|
|
1553
|
+
4. VERIFY: Run tests, type-checking, or build to validate changes
|
|
1554
|
+
5. COMPLETE: Summarize what was accomplished
|
|
1555
|
+
|
|
1556
|
+
CODE QUALITY:
|
|
1557
|
+
- Match existing code style and patterns in the project
|
|
1558
|
+
- Write immediately executable code (no TODOs or placeholders)
|
|
1559
|
+
- Prefer editing existing files over creating new ones
|
|
1560
|
+
- Use the project's established error handling patterns
|
|
1561
|
+
</development_workflow>
|
|
1562
|
+
|
|
1563
|
+
<communication>
|
|
1564
|
+
- Be concise and direct. Responses appear in a chat panel.
|
|
1565
|
+
- Focus on practical solutions over theoretical discussion.
|
|
1566
|
+
- When unable to complete a request, explain the limitation briefly and
|
|
1567
|
+
provide alternatives.
|
|
1568
|
+
- Use active language: "Analyzing...", "Searching..." instead of "Let me..."
|
|
1569
|
+
</communication>
|
|
1570
|
+
</system-reminder>`;
|
|
1571
|
+
|
|
1424
1572
|
// src/provider/duo-workflow-model.ts
|
|
1425
1573
|
var sessions = /* @__PURE__ */ new Map();
|
|
1426
1574
|
var UNKNOWN_USAGE = {
|
|
@@ -1442,6 +1590,8 @@ var DuoWorkflowModel = class {
|
|
|
1442
1590
|
#sentToolCallIds = /* @__PURE__ */ new Set();
|
|
1443
1591
|
#lastSentGoal = null;
|
|
1444
1592
|
#stateSessionId;
|
|
1593
|
+
#agentMode;
|
|
1594
|
+
#agentModeReminder;
|
|
1445
1595
|
constructor(modelId, client, cwd) {
|
|
1446
1596
|
this.modelId = modelId;
|
|
1447
1597
|
this.#client = client;
|
|
@@ -1481,6 +1631,8 @@ var DuoWorkflowModel = class {
|
|
|
1481
1631
|
this.#multiCallGroups.clear();
|
|
1482
1632
|
this.#sentToolCallIds.clear();
|
|
1483
1633
|
this.#lastSentGoal = null;
|
|
1634
|
+
this.#agentMode = void 0;
|
|
1635
|
+
this.#agentModeReminder = void 0;
|
|
1484
1636
|
this.#stateSessionId = sessionID;
|
|
1485
1637
|
}
|
|
1486
1638
|
const model = this;
|
|
@@ -1491,6 +1643,15 @@ var DuoWorkflowModel = class {
|
|
|
1491
1643
|
const onAbort = () => session.abort();
|
|
1492
1644
|
options.abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
1493
1645
|
try {
|
|
1646
|
+
if (!session.hasStarted) {
|
|
1647
|
+
model.#sentToolCallIds.clear();
|
|
1648
|
+
for (const r of toolResults) {
|
|
1649
|
+
if (!model.#pendingToolRequests.has(r.toolCallId)) {
|
|
1650
|
+
model.#sentToolCallIds.add(r.toolCallId);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
model.#lastSentGoal = null;
|
|
1654
|
+
}
|
|
1494
1655
|
const freshResults = toolResults.filter(
|
|
1495
1656
|
(r) => !model.#sentToolCallIds.has(r.toolCallId)
|
|
1496
1657
|
);
|
|
@@ -1530,7 +1691,41 @@ var DuoWorkflowModel = class {
|
|
|
1530
1691
|
if (!sentToolResults && isNewGoal) {
|
|
1531
1692
|
await session.ensureConnected(goal);
|
|
1532
1693
|
if (!session.hasStarted) {
|
|
1533
|
-
|
|
1694
|
+
const extraContext = [];
|
|
1695
|
+
extraContext.push(...buildSystemContext());
|
|
1696
|
+
const systemPrompt = extractSystemPrompt(options.prompt);
|
|
1697
|
+
if (systemPrompt) {
|
|
1698
|
+
extraContext.push({
|
|
1699
|
+
category: "agent_context",
|
|
1700
|
+
content: sanitizeSystemPrompt(systemPrompt),
|
|
1701
|
+
id: "agent_system_prompt",
|
|
1702
|
+
metadata: JSON.stringify({
|
|
1703
|
+
title: "Agent System Prompt",
|
|
1704
|
+
enabled: true,
|
|
1705
|
+
subType: "system_prompt"
|
|
1706
|
+
})
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
const agentReminders = extractAgentReminders(options.prompt);
|
|
1710
|
+
const modeReminder = detectLatestModeReminder(agentReminders);
|
|
1711
|
+
if (modeReminder) {
|
|
1712
|
+
model.#agentMode = modeReminder.mode;
|
|
1713
|
+
model.#agentModeReminder = modeReminder.reminder;
|
|
1714
|
+
}
|
|
1715
|
+
const remindersForContext = buildReminderContext(agentReminders, model.#agentModeReminder);
|
|
1716
|
+
if (remindersForContext.length > 0) {
|
|
1717
|
+
extraContext.push({
|
|
1718
|
+
category: "agent_context",
|
|
1719
|
+
content: sanitizeSystemPrompt(remindersForContext.join("\n\n")),
|
|
1720
|
+
id: "agent_reminders",
|
|
1721
|
+
metadata: JSON.stringify({
|
|
1722
|
+
title: "Agent Reminders",
|
|
1723
|
+
enabled: true,
|
|
1724
|
+
subType: "agent_reminders"
|
|
1725
|
+
})
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
session.sendStartRequest(goal, extraContext);
|
|
1534
1729
|
}
|
|
1535
1730
|
model.#lastSentGoal = goal;
|
|
1536
1731
|
}
|
|
@@ -1638,6 +1833,32 @@ var DuoWorkflowModel = class {
|
|
|
1638
1833
|
function sessionKey(instanceUrl, modelId, sessionID) {
|
|
1639
1834
|
return `${instanceUrl}::${modelId}::${sessionID}`;
|
|
1640
1835
|
}
|
|
1836
|
+
function detectLatestModeReminder(reminders) {
|
|
1837
|
+
let latest;
|
|
1838
|
+
for (const reminder of reminders) {
|
|
1839
|
+
const classification = classifyModeReminder(reminder);
|
|
1840
|
+
if (classification === "other") continue;
|
|
1841
|
+
latest = { mode: classification, reminder };
|
|
1842
|
+
}
|
|
1843
|
+
return latest;
|
|
1844
|
+
}
|
|
1845
|
+
function buildReminderContext(reminders, modeReminder) {
|
|
1846
|
+
const nonModeReminders = reminders.filter(
|
|
1847
|
+
(r) => classifyModeReminder(r) === "other"
|
|
1848
|
+
);
|
|
1849
|
+
if (!modeReminder) return nonModeReminders;
|
|
1850
|
+
return [...nonModeReminders, modeReminder];
|
|
1851
|
+
}
|
|
1852
|
+
function classifyModeReminder(reminder) {
|
|
1853
|
+
const text2 = reminder.toLowerCase();
|
|
1854
|
+
if (text2.includes("operational mode has changed from build to plan")) return "plan";
|
|
1855
|
+
if (text2.includes("operational mode has changed from plan to build")) return "build";
|
|
1856
|
+
if (text2.includes("you are no longer in read-only mode")) return "build";
|
|
1857
|
+
if (text2.includes("you are now in read-only mode")) return "plan";
|
|
1858
|
+
if (text2.includes("you are in read-only mode")) return "plan";
|
|
1859
|
+
if (text2.includes("you are permitted to make file changes")) return "build";
|
|
1860
|
+
return "other";
|
|
1861
|
+
}
|
|
1641
1862
|
|
|
1642
1863
|
// src/provider/index.ts
|
|
1643
1864
|
function createFallbackProvider(input = {}) {
|