pybao-cli 1.4.51 → 1.4.52
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/REPL-5YY25SRP.js +46 -0
- package/dist/{acp-2C774HUW.js → acp-QYEU24FQ.js} +31 -32
- package/dist/{acp-2C774HUW.js.map → acp-QYEU24FQ.js.map} +1 -1
- package/dist/{agentsValidate-RCXZBOU3.js → agentsValidate-755OIS5R.js} +7 -7
- package/dist/{ask-EIO3Q6ED.js → ask-DZPBLG26.js} +30 -31
- package/dist/{ask-EIO3Q6ED.js.map → ask-DZPBLG26.js.map} +1 -1
- package/dist/{autoUpdater-IIJMDPJE.js → autoUpdater-TYGW5WLI.js} +3 -3
- package/dist/{chunk-ZQAKMVXR.js → chunk-232RVCMQ.js} +3 -3
- package/dist/{chunk-DCSAQNHW.js → chunk-2BHQOW7O.js} +4 -4
- package/dist/{chunk-YTUV273Y.js → chunk-2HMRIM4L.js} +1607 -1434
- package/dist/chunk-2HMRIM4L.js.map +7 -0
- package/dist/{chunk-KNOCJVE6.js → chunk-4LMBJZXO.js} +1 -1
- package/dist/{chunk-GLS3ZXCS.js → chunk-BRKYVW3X.js} +3 -3
- package/dist/{chunk-6MFZG46W.js → chunk-CACSIWAN.js} +1 -1
- package/dist/{chunk-5RX62PWR.js → chunk-CIPSRLKQ.js} +1 -1
- package/dist/{chunk-YNVGW5VZ.js → chunk-DWNHYJME.js} +7 -7
- package/dist/{chunk-GTP62T63.js → chunk-EBSC7C7P.js} +3 -3
- package/dist/{chunk-LAQQRRQR.js → chunk-EF6YCOC4.js} +4 -4
- package/dist/{chunk-HY24VLOS.js → chunk-FTYUYN6L.js} +1 -1
- package/dist/{chunk-SQJJRPIY.js → chunk-FVNXJG2K.js} +5 -5
- package/dist/{chunk-G2FI43IB.js → chunk-GFD2DNDU.js} +2 -2
- package/dist/{chunk-U7MPXQO4.js → chunk-H6QOGXMT.js} +3 -3
- package/dist/{chunk-LEQNJ73H.js → chunk-KFQPMAFO.js} +3 -3
- package/dist/chunk-KMM3X3B5.js +448 -0
- package/dist/chunk-KMM3X3B5.js.map +7 -0
- package/dist/{chunk-HJDETBB6.js → chunk-LHL4VQUV.js} +1 -1
- package/dist/{chunk-LAFEZLTD.js → chunk-LTNUANEK.js} +141 -40
- package/dist/chunk-LTNUANEK.js.map +7 -0
- package/dist/{chunk-ESKTQXW6.js → chunk-NCHARDNR.js} +1 -1
- package/dist/{chunk-ESKTQXW6.js.map → chunk-NCHARDNR.js.map} +1 -1
- package/dist/{chunk-OBFMPMGW.js → chunk-NXAHOIRY.js} +2 -2
- package/dist/{chunk-OYCMMRZV.js → chunk-OBAKKVHK.js} +1 -1
- package/dist/{chunk-VUILI5ED.js → chunk-OMMS565Z.js} +22 -6
- package/dist/{chunk-VUILI5ED.js.map → chunk-OMMS565Z.js.map} +3 -3
- package/dist/{chunk-TMFNSMVV.js → chunk-OX2MB4Q2.js} +6 -6
- package/dist/{chunk-WSN5UCPE.js → chunk-R3QXCIEI.js} +1 -1
- package/dist/{chunk-SNKLRBWT.js → chunk-RCASKKDU.js} +2 -2
- package/dist/{chunk-WCKPMAYX.js → chunk-SEIO7WV7.js} +1 -1
- package/dist/{chunk-PKAVBIO7.js → chunk-VSGJTKCW.js} +1 -1
- package/dist/{chunk-MZ6UJROW.js → chunk-VTHHH4MB.js} +4 -4
- package/dist/{chunk-N6DXTYLR.js → chunk-WTHNVU5P.js} +3 -3
- package/dist/{chunk-KDKXQODT.js → chunk-XDWCDTQK.js} +2 -2
- package/dist/{cli-JPS6PPHH.js → cli-WGYHJPFZ.js} +94 -96
- package/dist/{cli-JPS6PPHH.js.map → cli-WGYHJPFZ.js.map} +1 -1
- package/dist/commands-6JEO2KAK.js +50 -0
- package/dist/{config-7RFJBYCF.js → config-AGAJCFQD.js} +4 -4
- package/dist/{context-L6IDU4ZF.js → context-ZOZD4S7X.js} +6 -6
- package/dist/{customCommands-2ZLDKEPS.js → customCommands-4GSMRRC4.js} +4 -4
- package/dist/{env-KKHHZ47D.js → env-RPUIAXPD.js} +2 -2
- package/dist/{file-QGJRLWJ7.js → file-HFMWEHAT.js} +4 -4
- package/dist/index.js +3 -3
- package/dist/{llm-HHSKS5JE.js → llm-CP2G3BZQ.js} +31 -32
- package/dist/{llm-HHSKS5JE.js.map → llm-CP2G3BZQ.js.map} +1 -1
- package/dist/{llmLazy-YV33JO4W.js → llmLazy-7GLWRPMO.js} +1 -1
- package/dist/{loader-YCEP5CYE.js → loader-RJUX5HRI.js} +4 -4
- package/dist/{lsp-TG3EZZOU.js → lsp-4PS6EN3E.js} +6 -6
- package/dist/{lspAnchor-T3544E7J.js → lspAnchor-QPHIEGKU.js} +6 -6
- package/dist/{mcp-SWYFI5FF.js → mcp-Z5GOR6OF.js} +7 -7
- package/dist/{mentionProcessor-IP6FAEXL.js → mentionProcessor-ZPSEF4XC.js} +5 -9
- package/dist/{mentionProcessor-IP6FAEXL.js.map → mentionProcessor-ZPSEF4XC.js.map} +1 -1
- package/dist/{messages-3B2ZJJKW.js → messages-7PZIS7S6.js} +1 -1
- package/dist/{model-HDJXCBKD.js → model-OMBFATWA.js} +5 -5
- package/dist/{openai-DF2BQT7X.js → openai-ZFDYT7H5.js} +5 -6
- package/dist/{outputStyles-GWUBSKDY.js → outputStyles-LK7EH6PS.js} +4 -4
- package/dist/{pluginRuntime-LKAAMJH2.js → pluginRuntime-PFAQAL5X.js} +6 -6
- package/dist/{pluginValidation-7GJA32YV.js → pluginValidation-F6UPGXZP.js} +6 -6
- package/dist/prompts-3I2J4KDX.js +52 -0
- package/dist/{pybAgentSessionLoad-BGIX7L4O.js → pybAgentSessionLoad-PQX3FKEC.js} +4 -4
- package/dist/{pybAgentSessionResume-VKK6VZSA.js → pybAgentSessionResume-FTGMQQ4B.js} +4 -4
- package/dist/{pybAgentStreamJsonSession-HDXJ4F5W.js → pybAgentStreamJsonSession-GQZLIIQS.js} +1 -1
- package/dist/{pybHooks-LEIAZ7D6.js → pybHooks-7SOKY4A3.js} +5 -5
- package/dist/query-MZKEYXCU.js +54 -0
- package/dist/{registry-C4V5AZ6N.js → registry-HEA2CKWA.js} +5 -5
- package/dist/{ripgrep-PWIRRS2Q.js → ripgrep-S4IOHQNI.js} +3 -3
- package/dist/{skillMarketplace-GZN6RWUY.js → skillMarketplace-GLH5WP4Y.js} +3 -3
- package/dist/{state-T7WQKFBG.js → state-DYO5HDW5.js} +2 -2
- package/dist/{theme-CLYLDSCI.js → theme-FMEMGZ2J.js} +5 -5
- package/dist/{toolPermissionSettings-UYHSUTC5.js → toolPermissionSettings-J2R2EOVJ.js} +6 -6
- package/dist/tools-OLRB4MMF.js +51 -0
- package/dist/{userInput-KZXH5O7V.js → userInput-IZB262ZP.js} +32 -33
- package/dist/{userInput-KZXH5O7V.js.map → userInput-IZB262ZP.js.map} +1 -1
- package/package.json +1 -1
- package/dist/REPL-6YEOSMEP.js +0 -47
- package/dist/chunk-ERMQRV55.js +0 -24
- package/dist/chunk-ERMQRV55.js.map +0 -7
- package/dist/chunk-L2FQX7IL.js +0 -1552
- package/dist/chunk-L2FQX7IL.js.map +0 -7
- package/dist/chunk-LAFEZLTD.js.map +0 -7
- package/dist/chunk-YTUV273Y.js.map +0 -7
- package/dist/commands-3IJYY2KF.js +0 -51
- package/dist/prompts-DB772NLS.js +0 -53
- package/dist/query-LVPGQYDL.js +0 -55
- package/dist/tools-SEWCFM5P.js +0 -52
- /package/dist/{REPL-6YEOSMEP.js.map → REPL-5YY25SRP.js.map} +0 -0
- /package/dist/{agentsValidate-RCXZBOU3.js.map → agentsValidate-755OIS5R.js.map} +0 -0
- /package/dist/{autoUpdater-IIJMDPJE.js.map → autoUpdater-TYGW5WLI.js.map} +0 -0
- /package/dist/{chunk-ZQAKMVXR.js.map → chunk-232RVCMQ.js.map} +0 -0
- /package/dist/{chunk-DCSAQNHW.js.map → chunk-2BHQOW7O.js.map} +0 -0
- /package/dist/{chunk-KNOCJVE6.js.map → chunk-4LMBJZXO.js.map} +0 -0
- /package/dist/{chunk-GLS3ZXCS.js.map → chunk-BRKYVW3X.js.map} +0 -0
- /package/dist/{chunk-6MFZG46W.js.map → chunk-CACSIWAN.js.map} +0 -0
- /package/dist/{chunk-5RX62PWR.js.map → chunk-CIPSRLKQ.js.map} +0 -0
- /package/dist/{chunk-YNVGW5VZ.js.map → chunk-DWNHYJME.js.map} +0 -0
- /package/dist/{chunk-GTP62T63.js.map → chunk-EBSC7C7P.js.map} +0 -0
- /package/dist/{chunk-LAQQRRQR.js.map → chunk-EF6YCOC4.js.map} +0 -0
- /package/dist/{chunk-HY24VLOS.js.map → chunk-FTYUYN6L.js.map} +0 -0
- /package/dist/{chunk-SQJJRPIY.js.map → chunk-FVNXJG2K.js.map} +0 -0
- /package/dist/{chunk-G2FI43IB.js.map → chunk-GFD2DNDU.js.map} +0 -0
- /package/dist/{chunk-U7MPXQO4.js.map → chunk-H6QOGXMT.js.map} +0 -0
- /package/dist/{chunk-LEQNJ73H.js.map → chunk-KFQPMAFO.js.map} +0 -0
- /package/dist/{chunk-HJDETBB6.js.map → chunk-LHL4VQUV.js.map} +0 -0
- /package/dist/{chunk-OBFMPMGW.js.map → chunk-NXAHOIRY.js.map} +0 -0
- /package/dist/{chunk-OYCMMRZV.js.map → chunk-OBAKKVHK.js.map} +0 -0
- /package/dist/{chunk-TMFNSMVV.js.map → chunk-OX2MB4Q2.js.map} +0 -0
- /package/dist/{chunk-WSN5UCPE.js.map → chunk-R3QXCIEI.js.map} +0 -0
- /package/dist/{chunk-SNKLRBWT.js.map → chunk-RCASKKDU.js.map} +0 -0
- /package/dist/{chunk-WCKPMAYX.js.map → chunk-SEIO7WV7.js.map} +0 -0
- /package/dist/{chunk-PKAVBIO7.js.map → chunk-VSGJTKCW.js.map} +0 -0
- /package/dist/{chunk-MZ6UJROW.js.map → chunk-VTHHH4MB.js.map} +0 -0
- /package/dist/{chunk-N6DXTYLR.js.map → chunk-WTHNVU5P.js.map} +0 -0
- /package/dist/{chunk-KDKXQODT.js.map → chunk-XDWCDTQK.js.map} +0 -0
- /package/dist/{commands-3IJYY2KF.js.map → commands-6JEO2KAK.js.map} +0 -0
- /package/dist/{config-7RFJBYCF.js.map → config-AGAJCFQD.js.map} +0 -0
- /package/dist/{context-L6IDU4ZF.js.map → context-ZOZD4S7X.js.map} +0 -0
- /package/dist/{customCommands-2ZLDKEPS.js.map → customCommands-4GSMRRC4.js.map} +0 -0
- /package/dist/{env-KKHHZ47D.js.map → env-RPUIAXPD.js.map} +0 -0
- /package/dist/{file-QGJRLWJ7.js.map → file-HFMWEHAT.js.map} +0 -0
- /package/dist/{llmLazy-YV33JO4W.js.map → llmLazy-7GLWRPMO.js.map} +0 -0
- /package/dist/{loader-YCEP5CYE.js.map → loader-RJUX5HRI.js.map} +0 -0
- /package/dist/{lsp-TG3EZZOU.js.map → lsp-4PS6EN3E.js.map} +0 -0
- /package/dist/{lspAnchor-T3544E7J.js.map → lspAnchor-QPHIEGKU.js.map} +0 -0
- /package/dist/{mcp-SWYFI5FF.js.map → mcp-Z5GOR6OF.js.map} +0 -0
- /package/dist/{messages-3B2ZJJKW.js.map → messages-7PZIS7S6.js.map} +0 -0
- /package/dist/{model-HDJXCBKD.js.map → model-OMBFATWA.js.map} +0 -0
- /package/dist/{openai-DF2BQT7X.js.map → openai-ZFDYT7H5.js.map} +0 -0
- /package/dist/{outputStyles-GWUBSKDY.js.map → outputStyles-LK7EH6PS.js.map} +0 -0
- /package/dist/{pluginRuntime-LKAAMJH2.js.map → pluginRuntime-PFAQAL5X.js.map} +0 -0
- /package/dist/{pluginValidation-7GJA32YV.js.map → pluginValidation-F6UPGXZP.js.map} +0 -0
- /package/dist/{prompts-DB772NLS.js.map → prompts-3I2J4KDX.js.map} +0 -0
- /package/dist/{pybAgentSessionLoad-BGIX7L4O.js.map → pybAgentSessionLoad-PQX3FKEC.js.map} +0 -0
- /package/dist/{pybAgentSessionResume-VKK6VZSA.js.map → pybAgentSessionResume-FTGMQQ4B.js.map} +0 -0
- /package/dist/{pybAgentStreamJsonSession-HDXJ4F5W.js.map → pybAgentStreamJsonSession-GQZLIIQS.js.map} +0 -0
- /package/dist/{pybHooks-LEIAZ7D6.js.map → pybHooks-7SOKY4A3.js.map} +0 -0
- /package/dist/{query-LVPGQYDL.js.map → query-MZKEYXCU.js.map} +0 -0
- /package/dist/{registry-C4V5AZ6N.js.map → registry-HEA2CKWA.js.map} +0 -0
- /package/dist/{ripgrep-PWIRRS2Q.js.map → ripgrep-S4IOHQNI.js.map} +0 -0
- /package/dist/{skillMarketplace-GZN6RWUY.js.map → skillMarketplace-GLH5WP4Y.js.map} +0 -0
- /package/dist/{state-T7WQKFBG.js.map → state-DYO5HDW5.js.map} +0 -0
- /package/dist/{theme-CLYLDSCI.js.map → theme-FMEMGZ2J.js.map} +0 -0
- /package/dist/{toolPermissionSettings-UYHSUTC5.js.map → toolPermissionSettings-J2R2EOVJ.js.map} +0 -0
- /package/dist/{tools-SEWCFM5P.js.map → tools-OLRB4MMF.js.map} +0 -0
package/dist/chunk-L2FQX7IL.js
DELETED
|
@@ -1,1552 +0,0 @@
|
|
|
1
|
-
import { createRequire as __pybCreateRequire } from "node:module";
|
|
2
|
-
const require = __pybCreateRequire(import.meta.url);
|
|
3
|
-
import {
|
|
4
|
-
getSessionState,
|
|
5
|
-
setSessionState
|
|
6
|
-
} from "./chunk-ERMQRV55.js";
|
|
7
|
-
import {
|
|
8
|
-
getPybAgentSessionId
|
|
9
|
-
} from "./chunk-B6IMQJZM.js";
|
|
10
|
-
import {
|
|
11
|
-
getCurrentProjectConfig,
|
|
12
|
-
getGlobalConfig
|
|
13
|
-
} from "./chunk-LEQNJ73H.js";
|
|
14
|
-
import {
|
|
15
|
-
debug
|
|
16
|
-
} from "./chunk-6MFZG46W.js";
|
|
17
|
-
import {
|
|
18
|
-
getCwd,
|
|
19
|
-
logError,
|
|
20
|
-
resolveXdgDataPath
|
|
21
|
-
} from "./chunk-HJDETBB6.js";
|
|
22
|
-
|
|
23
|
-
// src/utils/agent/storage.ts
|
|
24
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
25
|
-
import { join } from "path";
|
|
26
|
-
import { randomUUID } from "crypto";
|
|
27
|
-
function getConfigDirectory() {
|
|
28
|
-
const override = process.env.ANYKODE_CONFIG_DIR?.trim();
|
|
29
|
-
if (override) return override;
|
|
30
|
-
return resolveXdgDataPath("agents");
|
|
31
|
-
}
|
|
32
|
-
function getSessionId() {
|
|
33
|
-
return process.env.ANYKODE_SESSION_ID ?? "default-session";
|
|
34
|
-
}
|
|
35
|
-
function getAgentFilePath(agentId) {
|
|
36
|
-
const sessionId = getSessionId();
|
|
37
|
-
const filename = `${sessionId}-agent-${agentId}.json`;
|
|
38
|
-
const configDir = getConfigDirectory();
|
|
39
|
-
if (!existsSync(configDir)) {
|
|
40
|
-
mkdirSync(configDir, { recursive: true });
|
|
41
|
-
}
|
|
42
|
-
return join(configDir, filename);
|
|
43
|
-
}
|
|
44
|
-
function readAgentData(agentId) {
|
|
45
|
-
const filePath = getAgentFilePath(agentId);
|
|
46
|
-
if (!existsSync(filePath)) {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
try {
|
|
50
|
-
const content = readFileSync(filePath, "utf-8");
|
|
51
|
-
return JSON.parse(content);
|
|
52
|
-
} catch (error) {
|
|
53
|
-
logError(error);
|
|
54
|
-
debug.warn("AGENT_STORAGE_READ_FAILED", {
|
|
55
|
-
agentId,
|
|
56
|
-
error: error instanceof Error ? error.message : String(error)
|
|
57
|
-
});
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
function writeAgentData(agentId, data) {
|
|
62
|
-
const filePath = getAgentFilePath(agentId);
|
|
63
|
-
try {
|
|
64
|
-
writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
65
|
-
} catch (error) {
|
|
66
|
-
logError(error);
|
|
67
|
-
debug.warn("AGENT_STORAGE_WRITE_FAILED", {
|
|
68
|
-
agentId,
|
|
69
|
-
error: error instanceof Error ? error.message : String(error)
|
|
70
|
-
});
|
|
71
|
-
throw error;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
function getDefaultAgentId() {
|
|
75
|
-
return "default";
|
|
76
|
-
}
|
|
77
|
-
function resolveAgentId(agentId) {
|
|
78
|
-
return agentId || getDefaultAgentId();
|
|
79
|
-
}
|
|
80
|
-
function generateAgentId() {
|
|
81
|
-
return randomUUID();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// src/utils/session/taskStore.ts
|
|
85
|
-
import {
|
|
86
|
-
existsSync as existsSync2,
|
|
87
|
-
closeSync,
|
|
88
|
-
mkdirSync as mkdirSync2,
|
|
89
|
-
openSync,
|
|
90
|
-
readFileSync as readFileSync2,
|
|
91
|
-
readdirSync,
|
|
92
|
-
renameSync,
|
|
93
|
-
rmSync,
|
|
94
|
-
writeFileSync as writeFileSync2
|
|
95
|
-
} from "fs";
|
|
96
|
-
import { join as join2 } from "path";
|
|
97
|
-
import { homedir } from "os";
|
|
98
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
99
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
100
|
-
|
|
101
|
-
// src/utils/session/taskLockNative.ts
|
|
102
|
-
import { createRequire } from "module";
|
|
103
|
-
var require2 = createRequire(import.meta.url);
|
|
104
|
-
var getLinuxVariant = (report) => {
|
|
105
|
-
const glibc = report?.getReport?.()?.header?.glibcVersionRuntime;
|
|
106
|
-
return glibc ? "gnu" : "musl";
|
|
107
|
-
};
|
|
108
|
-
var resolveNativeLockPackage = (options) => {
|
|
109
|
-
const platform = options?.platform ?? process.platform;
|
|
110
|
-
const arch = options?.arch ?? process.arch;
|
|
111
|
-
const report = options?.report ?? process.report;
|
|
112
|
-
if (platform === "win32" && arch === "x64") {
|
|
113
|
-
return "@pyb/lock-win32-x64-msvc";
|
|
114
|
-
}
|
|
115
|
-
if (platform === "win32" && arch === "arm64") {
|
|
116
|
-
return "@pyb/lock-win32-arm64-msvc";
|
|
117
|
-
}
|
|
118
|
-
if (platform === "darwin" && arch === "x64") {
|
|
119
|
-
return "@pyb/lock-darwin-x64";
|
|
120
|
-
}
|
|
121
|
-
if (platform === "darwin" && arch === "arm64") {
|
|
122
|
-
return "@pyb/lock-darwin-arm64";
|
|
123
|
-
}
|
|
124
|
-
if (platform === "linux" && arch === "x64") {
|
|
125
|
-
const variant = getLinuxVariant(report);
|
|
126
|
-
return variant === "musl" ? "@pyb/lock-linux-x64-musl" : "@pyb/lock-linux-x64-gnu";
|
|
127
|
-
}
|
|
128
|
-
if (platform === "linux" && arch === "arm64") {
|
|
129
|
-
return "@pyb/lock-linux-arm64-gnu";
|
|
130
|
-
}
|
|
131
|
-
if (platform === "linux" && arch === "arm") {
|
|
132
|
-
return "@pyb/lock-linux-armv7-gnueabihf";
|
|
133
|
-
}
|
|
134
|
-
return null;
|
|
135
|
-
};
|
|
136
|
-
var cachedLock;
|
|
137
|
-
var loadNativeTaskLock = () => {
|
|
138
|
-
const globalLock = globalThis.__PYB_NATIVE_TASK_LOCK__;
|
|
139
|
-
if (globalLock) {
|
|
140
|
-
cachedLock = globalLock;
|
|
141
|
-
return cachedLock;
|
|
142
|
-
}
|
|
143
|
-
if (cachedLock !== void 0) return cachedLock;
|
|
144
|
-
const envPath = process.env.PYB_NATIVE_LOCK_NODE_PATH;
|
|
145
|
-
if (envPath) {
|
|
146
|
-
try {
|
|
147
|
-
const mod = require2(envPath);
|
|
148
|
-
const candidate = mod?.default ?? mod;
|
|
149
|
-
if (candidate && typeof candidate.lockExclusive === "function" && typeof candidate.tryLockExclusive === "function" && typeof candidate.unlock === "function") {
|
|
150
|
-
cachedLock = candidate;
|
|
151
|
-
return cachedLock;
|
|
152
|
-
}
|
|
153
|
-
} catch {
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
const packageName = resolveNativeLockPackage();
|
|
157
|
-
if (!packageName) {
|
|
158
|
-
cachedLock = null;
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
try {
|
|
162
|
-
const mod = require2(packageName);
|
|
163
|
-
const candidate = mod?.default ?? mod;
|
|
164
|
-
if (candidate && typeof candidate.lockExclusive === "function" && typeof candidate.tryLockExclusive === "function" && typeof candidate.unlock === "function") {
|
|
165
|
-
cachedLock = candidate;
|
|
166
|
-
return cachedLock;
|
|
167
|
-
}
|
|
168
|
-
} catch {
|
|
169
|
-
}
|
|
170
|
-
cachedLock = null;
|
|
171
|
-
return null;
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
// src/utils/session/taskStore.ts
|
|
175
|
-
var taskListIdStorage = new AsyncLocalStorage();
|
|
176
|
-
var taskListEnvStorage = new AsyncLocalStorage();
|
|
177
|
-
var nativeLock = null;
|
|
178
|
-
var nativeLockResolved = false;
|
|
179
|
-
function getNativeLock() {
|
|
180
|
-
const globalLock = globalThis.__PYB_NATIVE_TASK_LOCK__;
|
|
181
|
-
if (globalLock) {
|
|
182
|
-
nativeLock = globalLock;
|
|
183
|
-
nativeLockResolved = true;
|
|
184
|
-
return nativeLock;
|
|
185
|
-
}
|
|
186
|
-
if (nativeLockResolved) return nativeLock;
|
|
187
|
-
nativeLockResolved = true;
|
|
188
|
-
nativeLock = loadNativeTaskLock();
|
|
189
|
-
return nativeLock;
|
|
190
|
-
}
|
|
191
|
-
function generateTaskListId() {
|
|
192
|
-
return randomUUID2();
|
|
193
|
-
}
|
|
194
|
-
function runWithTaskListId(listId, handler) {
|
|
195
|
-
return taskListIdStorage.run(listId, () => {
|
|
196
|
-
const result = handler();
|
|
197
|
-
const asyncIterator = result && typeof result[Symbol.asyncIterator] === "function" ? result : null;
|
|
198
|
-
if (!asyncIterator) return result;
|
|
199
|
-
return {
|
|
200
|
-
[Symbol.asyncIterator]() {
|
|
201
|
-
const iterator = asyncIterator[Symbol.asyncIterator]();
|
|
202
|
-
return {
|
|
203
|
-
next: (value) => taskListIdStorage.run(listId, () => iterator.next(value)),
|
|
204
|
-
return: iterator.return ? (value) => taskListIdStorage.run(listId, () => iterator.return(value)) : void 0,
|
|
205
|
-
throw: iterator.throw ? (err) => taskListIdStorage.run(listId, () => iterator.throw(err)) : void 0
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
function runWithTaskListEnv(env, handler) {
|
|
212
|
-
return taskListEnvStorage.run(env, () => {
|
|
213
|
-
const result = handler();
|
|
214
|
-
const asyncIterator = result && typeof result[Symbol.asyncIterator] === "function" ? result : null;
|
|
215
|
-
if (!asyncIterator) return result;
|
|
216
|
-
return {
|
|
217
|
-
[Symbol.asyncIterator]() {
|
|
218
|
-
const iterator = asyncIterator[Symbol.asyncIterator]();
|
|
219
|
-
return {
|
|
220
|
-
next: (value) => taskListEnvStorage.run(env, () => iterator.next(value)),
|
|
221
|
-
return: iterator.return ? (value) => taskListEnvStorage.run(env, () => iterator.return(value)) : void 0,
|
|
222
|
-
throw: iterator.throw ? (err) => taskListEnvStorage.run(env, () => iterator.throw(err)) : void 0
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
};
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
var taskListLocks = /* @__PURE__ */ new Map();
|
|
229
|
-
var taskLockWaitBuffer = new Int32Array(new SharedArrayBuffer(4));
|
|
230
|
-
var DEFAULT_LOCK_TIMEOUT_MS = Number(
|
|
231
|
-
process.env.PYB_TASK_LOCK_TIMEOUT_MS ?? 5e3
|
|
232
|
-
);
|
|
233
|
-
var DEFAULT_LOCK_RETRY_MS = Number(process.env.PYB_TASK_LOCK_RETRY_MS ?? 50);
|
|
234
|
-
var TaskStoreLockError = class extends Error {
|
|
235
|
-
code = "TASK_STORE_LOCKED";
|
|
236
|
-
constructor(message) {
|
|
237
|
-
super(message);
|
|
238
|
-
this.name = "TaskStoreLockError";
|
|
239
|
-
}
|
|
240
|
-
};
|
|
241
|
-
var TaskStoreConflictError = class extends Error {
|
|
242
|
-
code = "TASK_STORE_CONFLICT";
|
|
243
|
-
taskId;
|
|
244
|
-
expectedBaseVersion;
|
|
245
|
-
actualBaseVersion;
|
|
246
|
-
constructor(params) {
|
|
247
|
-
super(
|
|
248
|
-
`Task ${params.taskId} baseVersion ${params.actual} does not match expected ${params.expected}`
|
|
249
|
-
);
|
|
250
|
-
this.name = "TaskStoreConflictError";
|
|
251
|
-
this.taskId = params.taskId;
|
|
252
|
-
this.expectedBaseVersion = params.expected;
|
|
253
|
-
this.actualBaseVersion = params.actual;
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
function getDefaultTaskListId(cwd) {
|
|
257
|
-
return getPybAgentSessionId();
|
|
258
|
-
}
|
|
259
|
-
function resolveGlobalTaskRootDir() {
|
|
260
|
-
const override = process.env.PYB_CONFIG_DIR ?? process.env.CLAUDE_CONFIG_DIR;
|
|
261
|
-
if (override) return override;
|
|
262
|
-
const home = homedir();
|
|
263
|
-
const pybDir = join2(home, ".pyb");
|
|
264
|
-
if (existsSync2(pybDir)) return pybDir;
|
|
265
|
-
const claudeDir = join2(home, ".claude");
|
|
266
|
-
if (existsSync2(claudeDir)) return claudeDir;
|
|
267
|
-
return pybDir;
|
|
268
|
-
}
|
|
269
|
-
function findBaseDirWithList(listId, candidates) {
|
|
270
|
-
for (const candidate of candidates) {
|
|
271
|
-
if (existsSync2(join2(candidate, listId))) return candidate;
|
|
272
|
-
}
|
|
273
|
-
return null;
|
|
274
|
-
}
|
|
275
|
-
function getTaskListContext(options) {
|
|
276
|
-
const env = options?.env ?? taskListEnvStorage.getStore() ?? process.env;
|
|
277
|
-
const globalConfig = options?.globalConfig ?? getGlobalConfig();
|
|
278
|
-
const projectConfig = options?.projectConfig ?? getCurrentProjectConfig();
|
|
279
|
-
const cwd = options?.cwd ?? getCwd();
|
|
280
|
-
const defaultId = getDefaultTaskListId(cwd);
|
|
281
|
-
const inputListId = options?.listId?.trim();
|
|
282
|
-
const contextListId = taskListIdStorage.getStore()?.trim();
|
|
283
|
-
const envId = env.PYB_TASK_LIST_ID?.trim() || env.CLAUDE_CODE_TASK_LIST_ID?.trim();
|
|
284
|
-
const globalId = globalConfig?.taskListId?.trim();
|
|
285
|
-
const projectId = projectConfig?.taskListId?.trim();
|
|
286
|
-
if (inputListId) {
|
|
287
|
-
if (inputListId === projectId || inputListId === defaultId) {
|
|
288
|
-
return { listId: inputListId, scope: "project", source: "input" };
|
|
289
|
-
}
|
|
290
|
-
if (inputListId === globalId || inputListId === envId) {
|
|
291
|
-
return { listId: inputListId, scope: "global", source: "input" };
|
|
292
|
-
}
|
|
293
|
-
return { listId: inputListId, scope: "project", source: "input" };
|
|
294
|
-
}
|
|
295
|
-
if (contextListId) {
|
|
296
|
-
return { listId: contextListId, scope: "project", source: "context" };
|
|
297
|
-
}
|
|
298
|
-
if (envId) return { listId: envId, scope: "global", source: "env" };
|
|
299
|
-
if (globalId) return { listId: globalId, scope: "global", source: "global" };
|
|
300
|
-
if (projectId) return { listId: projectId, scope: "project", source: "project" };
|
|
301
|
-
return { listId: defaultId, scope: "project", source: "default" };
|
|
302
|
-
}
|
|
303
|
-
function resolveTaskBaseDir(context, cwd) {
|
|
304
|
-
if (context.scope === "project") {
|
|
305
|
-
const pybDir = join2(cwd, ".pyb");
|
|
306
|
-
const claudeDir = join2(cwd, ".claude");
|
|
307
|
-
const hasProjectPyb = existsSync2(pybDir);
|
|
308
|
-
const hasProjectClaude = existsSync2(claudeDir);
|
|
309
|
-
const projectRoot = hasProjectPyb ? pybDir : hasProjectClaude ? claudeDir : pybDir;
|
|
310
|
-
const override2 = process.env.PYB_CONFIG_DIR ?? process.env.CLAUDE_CONFIG_DIR;
|
|
311
|
-
const defaultBaseDir2 = !hasProjectPyb && !hasProjectClaude && override2 ? join2(override2, "tasks") : join2(projectRoot, "tasks");
|
|
312
|
-
const candidates2 = [
|
|
313
|
-
join2(cwd, ".pyb", "tasks"),
|
|
314
|
-
join2(cwd, ".claude", "tasks"),
|
|
315
|
-
...override2 ? [join2(override2, "tasks")] : [],
|
|
316
|
-
resolveXdgDataPath("tasks")
|
|
317
|
-
];
|
|
318
|
-
return findBaseDirWithList(context.listId, candidates2) ?? defaultBaseDir2;
|
|
319
|
-
}
|
|
320
|
-
const override = process.env.PYB_CONFIG_DIR ?? process.env.CLAUDE_CONFIG_DIR;
|
|
321
|
-
const globalCandidates = override ? [join2(override, "tasks")] : [
|
|
322
|
-
join2(homedir(), ".pyb", "tasks"),
|
|
323
|
-
join2(homedir(), ".claude", "tasks")
|
|
324
|
-
];
|
|
325
|
-
const defaultBaseDir = join2(resolveGlobalTaskRootDir(), "tasks");
|
|
326
|
-
const candidates = [...globalCandidates, resolveXdgDataPath("tasks")];
|
|
327
|
-
return findBaseDirWithList(context.listId, candidates) ?? defaultBaseDir;
|
|
328
|
-
}
|
|
329
|
-
function buildTaskListPaths(baseDir, listId) {
|
|
330
|
-
const listDir = join2(baseDir, listId);
|
|
331
|
-
return {
|
|
332
|
-
listId,
|
|
333
|
-
baseDir,
|
|
334
|
-
listDir,
|
|
335
|
-
tasksDir: listDir,
|
|
336
|
-
lockPath: join2(listDir, ".lock"),
|
|
337
|
-
highwatermarkPath: join2(listDir, ".highwatermark")
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
function getTaskListPaths(listId) {
|
|
341
|
-
const context = getTaskListContext({ listId });
|
|
342
|
-
const cwd = getCwd();
|
|
343
|
-
const baseDir = resolveTaskBaseDir(context, cwd);
|
|
344
|
-
return buildTaskListPaths(baseDir, context.listId);
|
|
345
|
-
}
|
|
346
|
-
function initTaskListLockFile(options) {
|
|
347
|
-
const paths = getTaskListPaths(options?.listId);
|
|
348
|
-
ensureTaskListDirs(paths);
|
|
349
|
-
}
|
|
350
|
-
function sortReadPaths(paths) {
|
|
351
|
-
const priority = { pyb: 0, claude: 1, other: 2 };
|
|
352
|
-
return [...paths].sort((a, b) => priority[a.source] - priority[b.source]);
|
|
353
|
-
}
|
|
354
|
-
function resolveSourceForBaseDir(baseDir) {
|
|
355
|
-
if (baseDir.includes(`${join2(".pyb", "tasks")}`)) return "pyb";
|
|
356
|
-
if (baseDir.includes(`${join2(".claude", "tasks")}`)) return "claude";
|
|
357
|
-
if (baseDir.includes(`${join2(".pyb")}`)) return "pyb";
|
|
358
|
-
if (baseDir.includes(`${join2(".claude")}`)) return "claude";
|
|
359
|
-
return "other";
|
|
360
|
-
}
|
|
361
|
-
function getTaskListReadPaths(listId) {
|
|
362
|
-
const context = getTaskListContext({ listId });
|
|
363
|
-
const cwd = getCwd();
|
|
364
|
-
const projectPyb = join2(cwd, ".pyb", "tasks");
|
|
365
|
-
const projectClaude = join2(cwd, ".claude", "tasks");
|
|
366
|
-
if (context.scope === "project") {
|
|
367
|
-
const existing2 = [];
|
|
368
|
-
if (existsSync2(join2(projectPyb, context.listId))) {
|
|
369
|
-
existing2.push({
|
|
370
|
-
...buildTaskListPaths(projectPyb, context.listId),
|
|
371
|
-
source: "pyb"
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
if (existsSync2(join2(projectClaude, context.listId))) {
|
|
375
|
-
existing2.push({
|
|
376
|
-
...buildTaskListPaths(projectClaude, context.listId),
|
|
377
|
-
source: "claude"
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
if (existing2.length > 0) return sortReadPaths(existing2);
|
|
381
|
-
}
|
|
382
|
-
const override = process.env.PYB_CONFIG_DIR ?? process.env.CLAUDE_CONFIG_DIR;
|
|
383
|
-
const globalCandidates = override ? [
|
|
384
|
-
{
|
|
385
|
-
baseDir: join2(override, "tasks"),
|
|
386
|
-
source: resolveSourceForBaseDir(join2(override, "tasks"))
|
|
387
|
-
}
|
|
388
|
-
] : [
|
|
389
|
-
{ baseDir: join2(homedir(), ".pyb", "tasks"), source: "pyb" },
|
|
390
|
-
{ baseDir: join2(homedir(), ".claude", "tasks"), source: "claude" }
|
|
391
|
-
];
|
|
392
|
-
const candidates = [
|
|
393
|
-
...globalCandidates,
|
|
394
|
-
{ baseDir: resolveXdgDataPath("tasks"), source: "other" }
|
|
395
|
-
];
|
|
396
|
-
const existing = candidates.filter((candidate) => existsSync2(join2(candidate.baseDir, context.listId))).map((candidate) => ({
|
|
397
|
-
...buildTaskListPaths(candidate.baseDir, context.listId),
|
|
398
|
-
source: candidate.source
|
|
399
|
-
}));
|
|
400
|
-
if (existing.length > 0) return sortReadPaths(existing);
|
|
401
|
-
const baseDir = resolveTaskBaseDir(context, cwd);
|
|
402
|
-
return [
|
|
403
|
-
{
|
|
404
|
-
...buildTaskListPaths(baseDir, context.listId),
|
|
405
|
-
source: resolveSourceForBaseDir(baseDir)
|
|
406
|
-
}
|
|
407
|
-
];
|
|
408
|
-
}
|
|
409
|
-
function ensureTaskListDirs(paths) {
|
|
410
|
-
mkdirSync2(paths.listDir, { recursive: true });
|
|
411
|
-
if (!existsSync2(paths.lockPath)) {
|
|
412
|
-
writeFileSync2(paths.lockPath, "");
|
|
413
|
-
}
|
|
414
|
-
cleanupLegacyFiles(paths);
|
|
415
|
-
}
|
|
416
|
-
function readJson(path) {
|
|
417
|
-
if (!existsSync2(path)) return null;
|
|
418
|
-
try {
|
|
419
|
-
const raw = readFileSync2(path, "utf8");
|
|
420
|
-
return JSON.parse(raw);
|
|
421
|
-
} catch {
|
|
422
|
-
return null;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
function writeJson(path, value) {
|
|
426
|
-
writeFileSync2(path, JSON.stringify(value, null, 2), "utf8");
|
|
427
|
-
}
|
|
428
|
-
function writeJsonAtomic(path, value) {
|
|
429
|
-
const tempPath = `${path}.${randomUUID2()}.tmp`;
|
|
430
|
-
writeFileSync2(tempPath, JSON.stringify(value, null, 2), "utf8");
|
|
431
|
-
renameSync(tempPath, path);
|
|
432
|
-
}
|
|
433
|
-
function readHighwatermark(paths) {
|
|
434
|
-
if (!existsSync2(paths.highwatermarkPath)) return null;
|
|
435
|
-
const raw = readFileSync2(paths.highwatermarkPath, "utf8").trim();
|
|
436
|
-
const value = Number(raw);
|
|
437
|
-
if (!Number.isFinite(value) || value < 0) return null;
|
|
438
|
-
return value;
|
|
439
|
-
}
|
|
440
|
-
function writeHighwatermark(paths, value) {
|
|
441
|
-
ensureTaskListDirs(paths);
|
|
442
|
-
writeFileSync2(paths.highwatermarkPath, String(value), "utf8");
|
|
443
|
-
}
|
|
444
|
-
function updateHighwatermark(paths, value) {
|
|
445
|
-
if (!Number.isFinite(value) || value < 0) return;
|
|
446
|
-
const current = readHighwatermark(paths);
|
|
447
|
-
const next = current !== null ? Math.max(current, value) : value;
|
|
448
|
-
writeHighwatermark(paths, next);
|
|
449
|
-
}
|
|
450
|
-
function readTaskFile(path) {
|
|
451
|
-
const data = readJson(path);
|
|
452
|
-
if (!data || !data.id || !data.subject && !data.title || !data.status) {
|
|
453
|
-
return null;
|
|
454
|
-
}
|
|
455
|
-
const status = data.status === "open" ? "pending" : data.status;
|
|
456
|
-
if (status !== "pending" && status !== "in_progress" && status !== "blocked" && status !== "done" && status !== "archived") {
|
|
457
|
-
return null;
|
|
458
|
-
}
|
|
459
|
-
const subject = data.subject ?? data.title;
|
|
460
|
-
const activeForm = data.activeForm;
|
|
461
|
-
const baseVersion = typeof data.baseVersion === "number" ? data.baseVersion : typeof data.version === "number" ? data.version : 1;
|
|
462
|
-
const version = typeof data.version === "number" ? data.version : baseVersion;
|
|
463
|
-
const createdAt = typeof data.createdAt === "number" ? data.createdAt : Date.now();
|
|
464
|
-
const updatedAt = typeof data.updatedAt === "number" ? data.updatedAt : createdAt;
|
|
465
|
-
const normalized = {
|
|
466
|
-
id: data.id,
|
|
467
|
-
subject,
|
|
468
|
-
description: data.description,
|
|
469
|
-
activeForm,
|
|
470
|
-
status,
|
|
471
|
-
tags: data.tags,
|
|
472
|
-
assignee: data.assignee,
|
|
473
|
-
metadata: data.metadata,
|
|
474
|
-
archived: data.archived,
|
|
475
|
-
blocks: data.blocks ?? [],
|
|
476
|
-
blockedBy: data.blockedBy ?? [],
|
|
477
|
-
parent: data.parent,
|
|
478
|
-
related: data.related ?? [],
|
|
479
|
-
createdAt,
|
|
480
|
-
updatedAt,
|
|
481
|
-
baseVersion,
|
|
482
|
-
version,
|
|
483
|
-
legacyTodoId: data.legacyTodoId
|
|
484
|
-
};
|
|
485
|
-
delete normalized.priority;
|
|
486
|
-
return normalized;
|
|
487
|
-
}
|
|
488
|
-
function deriveBlocks(tasks) {
|
|
489
|
-
const map = /* @__PURE__ */ new Map();
|
|
490
|
-
for (const task of tasks) {
|
|
491
|
-
map.set(task.id, []);
|
|
492
|
-
}
|
|
493
|
-
for (const task of tasks) {
|
|
494
|
-
const blockers = task.blockedBy ?? [];
|
|
495
|
-
for (const blockerId of blockers) {
|
|
496
|
-
const list = map.get(blockerId);
|
|
497
|
-
if (list) {
|
|
498
|
-
list.push(task.id);
|
|
499
|
-
} else {
|
|
500
|
-
map.set(blockerId, [task.id]);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
for (const [key, list] of map.entries()) {
|
|
505
|
-
map.set(
|
|
506
|
-
key,
|
|
507
|
-
list.sort((a, b) => Number(a) - Number(b))
|
|
508
|
-
);
|
|
509
|
-
}
|
|
510
|
-
return map;
|
|
511
|
-
}
|
|
512
|
-
function areBlocksEqual(a, b) {
|
|
513
|
-
const left = a ?? [];
|
|
514
|
-
const right = b ?? [];
|
|
515
|
-
if (left.length !== right.length) return false;
|
|
516
|
-
for (let i = 0; i < left.length; i += 1) {
|
|
517
|
-
if (left[i] !== right[i]) return false;
|
|
518
|
-
}
|
|
519
|
-
return true;
|
|
520
|
-
}
|
|
521
|
-
function readTasksFromDisk(paths) {
|
|
522
|
-
if (!existsSync2(paths.tasksDir)) return [];
|
|
523
|
-
const files = readdirSync(paths.tasksDir).filter((file) => file.endsWith(".json"));
|
|
524
|
-
return files.map((file) => readTaskFile(join2(paths.tasksDir, file))).filter(Boolean);
|
|
525
|
-
}
|
|
526
|
-
function applyBlocksMap(tasks, map) {
|
|
527
|
-
return tasks.map((task) => ({
|
|
528
|
-
...task,
|
|
529
|
-
blocks: map.get(task.id) ?? []
|
|
530
|
-
}));
|
|
531
|
-
}
|
|
532
|
-
function persistBlocksMap(paths, map) {
|
|
533
|
-
ensureTaskListDirs(paths);
|
|
534
|
-
const files = readdirSync(paths.tasksDir).filter((file) => file.endsWith(".json"));
|
|
535
|
-
let updated = 0;
|
|
536
|
-
for (const file of files) {
|
|
537
|
-
const path = join2(paths.tasksDir, file);
|
|
538
|
-
const raw = readJson(path);
|
|
539
|
-
if (!raw || !raw.id) continue;
|
|
540
|
-
const nextBlocks = map.get(raw.id) ?? [];
|
|
541
|
-
const currentBlocks = Array.isArray(raw.blocks) ? raw.blocks : [];
|
|
542
|
-
if (areBlocksEqual(currentBlocks, nextBlocks)) continue;
|
|
543
|
-
raw.blocks = nextBlocks;
|
|
544
|
-
writeJson(path, raw);
|
|
545
|
-
updated += 1;
|
|
546
|
-
}
|
|
547
|
-
return updated;
|
|
548
|
-
}
|
|
549
|
-
function rebuildBlocksForPaths(paths) {
|
|
550
|
-
const tasks = readTasksFromDisk(paths);
|
|
551
|
-
const map = deriveBlocks(tasks);
|
|
552
|
-
const updated = persistBlocksMap(paths, map);
|
|
553
|
-
return { tasks: applyBlocksMap(tasks, map), updated };
|
|
554
|
-
}
|
|
555
|
-
var legacyFiles = ["index.json", "meta.json", "events.jsonl"];
|
|
556
|
-
function cleanupLegacyFiles(paths) {
|
|
557
|
-
for (const file of legacyFiles) {
|
|
558
|
-
const path = join2(paths.listDir, file);
|
|
559
|
-
if (existsSync2(path)) {
|
|
560
|
-
rmSync(path, { force: true });
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
function getMaxIdFromTasksDir(paths) {
|
|
565
|
-
if (!existsSync2(paths.tasksDir)) return 0;
|
|
566
|
-
const files = readdirSync(paths.tasksDir).filter((file) => file.endsWith(".json"));
|
|
567
|
-
let maxId = 0;
|
|
568
|
-
for (const file of files) {
|
|
569
|
-
const numeric = Number(file.replace(/\.json$/, ""));
|
|
570
|
-
if (Number.isFinite(numeric)) {
|
|
571
|
-
maxId = Math.max(maxId, numeric);
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
return maxId;
|
|
575
|
-
}
|
|
576
|
-
function getNextTaskId(paths) {
|
|
577
|
-
const highwatermark = readHighwatermark(paths) ?? 0;
|
|
578
|
-
const maxId = getMaxIdFromTasksDir(paths);
|
|
579
|
-
return Math.max(highwatermark, maxId) + 1;
|
|
580
|
-
}
|
|
581
|
-
function acquireLock(paths) {
|
|
582
|
-
ensureTaskListDirs(paths);
|
|
583
|
-
const start = Date.now();
|
|
584
|
-
while (taskListLocks.has(paths.lockPath)) {
|
|
585
|
-
const elapsed = Date.now() - start;
|
|
586
|
-
if (elapsed >= DEFAULT_LOCK_TIMEOUT_MS) {
|
|
587
|
-
throw new TaskStoreLockError("Task list is locked");
|
|
588
|
-
}
|
|
589
|
-
Atomics.wait(taskLockWaitBuffer, 0, 0, DEFAULT_LOCK_RETRY_MS);
|
|
590
|
-
}
|
|
591
|
-
const native = getNativeLock();
|
|
592
|
-
if (!native) {
|
|
593
|
-
const platformLabel = `${process.platform}-${process.arch}`;
|
|
594
|
-
const packageName = resolveNativeLockPackage();
|
|
595
|
-
if (packageName) {
|
|
596
|
-
throw new TaskStoreLockError(
|
|
597
|
-
`Native lock unavailable. Missing package ${packageName} for ${platformLabel}`
|
|
598
|
-
);
|
|
599
|
-
}
|
|
600
|
-
throw new TaskStoreLockError(
|
|
601
|
-
`Native lock unavailable. Unsupported platform ${platformLabel}`
|
|
602
|
-
);
|
|
603
|
-
}
|
|
604
|
-
if (native.tryLockExclusivePath && native.unlockHandle) {
|
|
605
|
-
const lockKey = paths.lockPath;
|
|
606
|
-
while (true) {
|
|
607
|
-
try {
|
|
608
|
-
const handle = native.tryLockExclusivePath(lockKey);
|
|
609
|
-
if (handle > 0) {
|
|
610
|
-
taskListLocks.set(paths.lockPath, { handle, mode: "nativeHandle" });
|
|
611
|
-
return;
|
|
612
|
-
}
|
|
613
|
-
} catch {
|
|
614
|
-
}
|
|
615
|
-
const elapsed = Date.now() - start;
|
|
616
|
-
if (elapsed >= DEFAULT_LOCK_TIMEOUT_MS) {
|
|
617
|
-
throw new TaskStoreLockError("Task list is locked");
|
|
618
|
-
}
|
|
619
|
-
Atomics.wait(taskLockWaitBuffer, 0, 0, DEFAULT_LOCK_RETRY_MS);
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
while (true) {
|
|
623
|
-
const handle = openSync(paths.lockPath, "r+");
|
|
624
|
-
try {
|
|
625
|
-
const ok = native.tryLockExclusive(handle);
|
|
626
|
-
if (ok) {
|
|
627
|
-
taskListLocks.set(paths.lockPath, { handle, mode: "fd" });
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
try {
|
|
631
|
-
closeSync(handle);
|
|
632
|
-
} catch {
|
|
633
|
-
}
|
|
634
|
-
const elapsed = Date.now() - start;
|
|
635
|
-
if (elapsed >= DEFAULT_LOCK_TIMEOUT_MS) {
|
|
636
|
-
throw new TaskStoreLockError("Task list is locked");
|
|
637
|
-
}
|
|
638
|
-
Atomics.wait(taskLockWaitBuffer, 0, 0, DEFAULT_LOCK_RETRY_MS);
|
|
639
|
-
} catch {
|
|
640
|
-
try {
|
|
641
|
-
closeSync(handle);
|
|
642
|
-
} catch {
|
|
643
|
-
}
|
|
644
|
-
const elapsed = Date.now() - start;
|
|
645
|
-
if (elapsed >= DEFAULT_LOCK_TIMEOUT_MS) {
|
|
646
|
-
throw new TaskStoreLockError("Task list is locked");
|
|
647
|
-
}
|
|
648
|
-
Atomics.wait(taskLockWaitBuffer, 0, 0, DEFAULT_LOCK_RETRY_MS);
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
function releaseLock(paths) {
|
|
653
|
-
const lock = taskListLocks.get(paths.lockPath);
|
|
654
|
-
if (lock !== void 0) {
|
|
655
|
-
const native = getNativeLock();
|
|
656
|
-
if (native && lock.mode === "nativeHandle" && native.unlockHandle) {
|
|
657
|
-
try {
|
|
658
|
-
native.unlockHandle(lock.handle);
|
|
659
|
-
} catch {
|
|
660
|
-
}
|
|
661
|
-
} else {
|
|
662
|
-
if (native) {
|
|
663
|
-
try {
|
|
664
|
-
native.unlock(lock.handle);
|
|
665
|
-
} catch {
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
closeSync(lock.handle);
|
|
669
|
-
}
|
|
670
|
-
taskListLocks.delete(paths.lockPath);
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
function withTaskListLock(paths, handler) {
|
|
674
|
-
acquireLock(paths);
|
|
675
|
-
try {
|
|
676
|
-
return handler();
|
|
677
|
-
} finally {
|
|
678
|
-
releaseLock(paths);
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
function mergeMetadata(existing, updates) {
|
|
682
|
-
if (!updates) return existing;
|
|
683
|
-
const next = { ...existing ?? {} };
|
|
684
|
-
for (const [key, value] of Object.entries(updates)) {
|
|
685
|
-
if (value === null) {
|
|
686
|
-
delete next[key];
|
|
687
|
-
} else {
|
|
688
|
-
next[key] = value;
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
return Object.keys(next).length > 0 ? next : void 0;
|
|
692
|
-
}
|
|
693
|
-
function applyTaskUpdates(task, updates, nextBaseVersion) {
|
|
694
|
-
const now = updates.updatedAt ?? Date.now();
|
|
695
|
-
const subject = updates.subject ?? task.subject;
|
|
696
|
-
const activeForm = updates.activeForm ?? task.activeForm;
|
|
697
|
-
const blockedBy = updates.blockedBy ?? task.blockedBy ?? [];
|
|
698
|
-
const nextStatus = updates.status ?? task.status;
|
|
699
|
-
const status = nextStatus === "open" ? task.status : nextStatus;
|
|
700
|
-
return {
|
|
701
|
-
...task,
|
|
702
|
-
subject,
|
|
703
|
-
description: updates.description ?? task.description,
|
|
704
|
-
activeForm,
|
|
705
|
-
status,
|
|
706
|
-
tags: updates.tags ?? task.tags,
|
|
707
|
-
assignee: updates.assignee ?? task.assignee,
|
|
708
|
-
metadata: mergeMetadata(task.metadata, updates.metadata),
|
|
709
|
-
archived: updates.archived ?? task.archived,
|
|
710
|
-
blocks: task.blocks ?? [],
|
|
711
|
-
blockedBy,
|
|
712
|
-
parent: updates.parent ?? task.parent,
|
|
713
|
-
related: updates.related ?? task.related,
|
|
714
|
-
updatedAt: now,
|
|
715
|
-
baseVersion: nextBaseVersion,
|
|
716
|
-
version: nextBaseVersion,
|
|
717
|
-
legacyTodoId: updates.legacyTodoId ?? task.legacyTodoId
|
|
718
|
-
};
|
|
719
|
-
}
|
|
720
|
-
function createTask(input, options) {
|
|
721
|
-
if (input.status === "open") {
|
|
722
|
-
throw new Error("Task status open is not supported");
|
|
723
|
-
}
|
|
724
|
-
const paths = getTaskListPaths(options?.listId);
|
|
725
|
-
return withTaskListLock(paths, () => {
|
|
726
|
-
ensureTaskListDirs(paths);
|
|
727
|
-
const id = String(getNextTaskId(paths));
|
|
728
|
-
const now = Date.now();
|
|
729
|
-
const subject = input.subject;
|
|
730
|
-
if (!subject) {
|
|
731
|
-
throw new Error("Task subject is required");
|
|
732
|
-
}
|
|
733
|
-
const activeForm = input.activeForm;
|
|
734
|
-
const status = input.status ?? "pending";
|
|
735
|
-
if (status === "open") {
|
|
736
|
-
throw new Error("Task status open is not supported");
|
|
737
|
-
}
|
|
738
|
-
const record = {
|
|
739
|
-
id,
|
|
740
|
-
subject,
|
|
741
|
-
description: input.description,
|
|
742
|
-
activeForm,
|
|
743
|
-
status,
|
|
744
|
-
tags: input.tags,
|
|
745
|
-
assignee: input.assignee,
|
|
746
|
-
metadata: input.metadata,
|
|
747
|
-
archived: input.archived,
|
|
748
|
-
blocks: [],
|
|
749
|
-
blockedBy: input.blockedBy ?? [],
|
|
750
|
-
parent: input.parent,
|
|
751
|
-
related: input.related,
|
|
752
|
-
createdAt: now,
|
|
753
|
-
updatedAt: now,
|
|
754
|
-
baseVersion: 1,
|
|
755
|
-
version: 1,
|
|
756
|
-
legacyTodoId: input.legacyTodoId
|
|
757
|
-
};
|
|
758
|
-
writeJson(join2(paths.tasksDir, `${id}.json`), record);
|
|
759
|
-
updateHighwatermark(paths, Number(id));
|
|
760
|
-
const { tasks } = rebuildBlocksForPaths(paths);
|
|
761
|
-
const refreshed = tasks.find((task) => task.id === record.id);
|
|
762
|
-
return refreshed ?? record;
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
function getTask(id, options) {
|
|
766
|
-
const pathsList = getTaskListReadPaths(options?.listId);
|
|
767
|
-
for (const paths of pathsList) {
|
|
768
|
-
const taskPath = join2(paths.tasksDir, `${id}.json`);
|
|
769
|
-
if (!existsSync2(taskPath)) continue;
|
|
770
|
-
const task = readTaskFile(taskPath);
|
|
771
|
-
if (task) return task;
|
|
772
|
-
}
|
|
773
|
-
return null;
|
|
774
|
-
}
|
|
775
|
-
function listTasks(options) {
|
|
776
|
-
const pathsList = getTaskListReadPaths(options?.listId);
|
|
777
|
-
const merged = /* @__PURE__ */ new Map();
|
|
778
|
-
for (const paths of pathsList) {
|
|
779
|
-
const tasks = readTasksFromDisk(paths);
|
|
780
|
-
const map = deriveBlocks(tasks);
|
|
781
|
-
const derived = applyBlocksMap(tasks, map);
|
|
782
|
-
const needsPersist = tasks.some((task) => {
|
|
783
|
-
const nextBlocks = map.get(task.id) ?? [];
|
|
784
|
-
return !areBlocksEqual(task.blocks, nextBlocks);
|
|
785
|
-
});
|
|
786
|
-
const ready = needsPersist ? withTaskListLock(paths, () => rebuildBlocksForPaths(paths).tasks) : derived;
|
|
787
|
-
for (const task of ready) {
|
|
788
|
-
if (!merged.has(task.id)) {
|
|
789
|
-
merged.set(task.id, task);
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
return Array.from(merged.values()).sort((a, b) => Number(a.id) - Number(b.id));
|
|
794
|
-
}
|
|
795
|
-
function updateTask(id, updates, options) {
|
|
796
|
-
if (updates.status === "open") {
|
|
797
|
-
throw new Error("Task status open is not supported");
|
|
798
|
-
}
|
|
799
|
-
const pathsList = getTaskListReadPaths(options?.listId);
|
|
800
|
-
const target = pathsList.find(
|
|
801
|
-
(paths) => existsSync2(join2(paths.tasksDir, `${id}.json`))
|
|
802
|
-
);
|
|
803
|
-
if (!target) {
|
|
804
|
-
throw new Error(`Task ${id} not found`);
|
|
805
|
-
}
|
|
806
|
-
return withTaskListLock(target, () => {
|
|
807
|
-
const existing = readTaskFile(join2(target.tasksDir, `${id}.json`));
|
|
808
|
-
if (!existing) {
|
|
809
|
-
throw new Error(`Task ${id} not found`);
|
|
810
|
-
}
|
|
811
|
-
const requestedBase = updates.baseVersion;
|
|
812
|
-
const hasConflict = typeof requestedBase === "number" && requestedBase !== existing.baseVersion;
|
|
813
|
-
if (hasConflict && !options?.allowMerge) {
|
|
814
|
-
throw new TaskStoreConflictError({
|
|
815
|
-
taskId: id,
|
|
816
|
-
expected: requestedBase,
|
|
817
|
-
actual: existing.baseVersion
|
|
818
|
-
});
|
|
819
|
-
}
|
|
820
|
-
if (hasConflict && options?.allowMerge) {
|
|
821
|
-
const updateTimestamp = updates.updatedAt ?? Date.now();
|
|
822
|
-
if (updateTimestamp <= existing.updatedAt) {
|
|
823
|
-
return { task: existing, conflict: true };
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
const nextBaseVersion = existing.baseVersion + 1;
|
|
827
|
-
const updated = applyTaskUpdates(existing, updates, nextBaseVersion);
|
|
828
|
-
writeJsonAtomic(join2(target.tasksDir, `${id}.json`), updated);
|
|
829
|
-
const { tasks } = rebuildBlocksForPaths(target);
|
|
830
|
-
const refreshed = tasks.find((task) => task.id === updated.id);
|
|
831
|
-
return { task: refreshed ?? updated, conflict: hasConflict };
|
|
832
|
-
});
|
|
833
|
-
}
|
|
834
|
-
function rebuildTaskBlocks(options) {
|
|
835
|
-
const paths = getTaskListPaths(options?.listId);
|
|
836
|
-
return withTaskListLock(paths, () => {
|
|
837
|
-
ensureTaskListDirs(paths);
|
|
838
|
-
const { tasks, updated } = rebuildBlocksForPaths(paths);
|
|
839
|
-
return { updated, total: tasks.length };
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
// src/utils/session/todoStorage.ts
|
|
844
|
-
var TODO_STORAGE_KEY = "todos";
|
|
845
|
-
var TODO_CONFIG_KEY = "todoConfig";
|
|
846
|
-
var TODO_MIGRATION_KEY = "todoMigration";
|
|
847
|
-
var DEFAULT_CONFIG = {
|
|
848
|
-
maxTodos: 100,
|
|
849
|
-
autoArchiveCompleted: false,
|
|
850
|
-
sortBy: "status",
|
|
851
|
-
sortOrder: "desc"
|
|
852
|
-
};
|
|
853
|
-
var todoCache = null;
|
|
854
|
-
var cacheTimestamp = 0;
|
|
855
|
-
var CACHE_TTL = 5e3;
|
|
856
|
-
function isTodoCompatEnabled(env = process.env) {
|
|
857
|
-
const raw = String(
|
|
858
|
-
env.PYB_TODO_COMPAT ?? env.CLAUDE_CODE_TODO_COMPAT ?? ""
|
|
859
|
-
).trim().toLowerCase();
|
|
860
|
-
if (!raw) return false;
|
|
861
|
-
if (["0", "false", "no", "off"].includes(raw)) return false;
|
|
862
|
-
if (["1", "true", "yes", "on"].includes(raw)) return true;
|
|
863
|
-
return true;
|
|
864
|
-
}
|
|
865
|
-
function toTaskStatus(status) {
|
|
866
|
-
if (status === "completed") return "done";
|
|
867
|
-
if (status === "in_progress") return "in_progress";
|
|
868
|
-
return "pending";
|
|
869
|
-
}
|
|
870
|
-
function syncTodosToTasks(todos, options) {
|
|
871
|
-
const { listId } = getTaskListPaths(options?.listId);
|
|
872
|
-
const tasks = listTasks({ listId });
|
|
873
|
-
const byLegacyId = new Map(
|
|
874
|
-
tasks.filter((task) => task.legacyTodoId).map((task) => [task.legacyTodoId, task])
|
|
875
|
-
);
|
|
876
|
-
let created = 0;
|
|
877
|
-
let updated = 0;
|
|
878
|
-
for (const todo of todos) {
|
|
879
|
-
const existing = byLegacyId.get(todo.id);
|
|
880
|
-
if (!existing) {
|
|
881
|
-
createTask(
|
|
882
|
-
{
|
|
883
|
-
subject: todo.content,
|
|
884
|
-
description: todo.content,
|
|
885
|
-
activeForm: todo.activeForm,
|
|
886
|
-
status: toTaskStatus(todo.status),
|
|
887
|
-
legacyTodoId: todo.id
|
|
888
|
-
},
|
|
889
|
-
{ listId }
|
|
890
|
-
);
|
|
891
|
-
created += 1;
|
|
892
|
-
} else {
|
|
893
|
-
updateTask(
|
|
894
|
-
existing.id,
|
|
895
|
-
{
|
|
896
|
-
subject: todo.content,
|
|
897
|
-
description: todo.content,
|
|
898
|
-
activeForm: todo.activeForm,
|
|
899
|
-
status: toTaskStatus(todo.status),
|
|
900
|
-
legacyTodoId: todo.id
|
|
901
|
-
},
|
|
902
|
-
{ listId, allowMerge: true }
|
|
903
|
-
);
|
|
904
|
-
updated += 1;
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
return { listId, created, updated };
|
|
908
|
-
}
|
|
909
|
-
function migrateTodosToTasks(options) {
|
|
910
|
-
const { listId } = getTaskListPaths(options?.listId);
|
|
911
|
-
const migrationKey = `${listId}:${options?.agentId ?? "default"}`;
|
|
912
|
-
const sessionState = getSessionState();
|
|
913
|
-
const migrationState = sessionState[TODO_MIGRATION_KEY] ?? {};
|
|
914
|
-
if (migrationState[migrationKey]?.completedAt) {
|
|
915
|
-
return { listId, migrated: false, created: 0 };
|
|
916
|
-
}
|
|
917
|
-
const todos = getTodos(options?.agentId);
|
|
918
|
-
const result = syncTodosToTasks(todos, { listId });
|
|
919
|
-
setSessionState({
|
|
920
|
-
...sessionState,
|
|
921
|
-
[TODO_MIGRATION_KEY]: {
|
|
922
|
-
...migrationState,
|
|
923
|
-
[migrationKey]: {
|
|
924
|
-
completedAt: Date.now(),
|
|
925
|
-
sourceCount: todos.length
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
});
|
|
929
|
-
return { listId, migrated: true, created: result.created };
|
|
930
|
-
}
|
|
931
|
-
function invalidateCache() {
|
|
932
|
-
todoCache = null;
|
|
933
|
-
cacheTimestamp = 0;
|
|
934
|
-
}
|
|
935
|
-
function updateMetrics(operation, cacheHit = false) {
|
|
936
|
-
const sessionState = getSessionState();
|
|
937
|
-
const metrics = sessionState.todoMetrics || {
|
|
938
|
-
totalOperations: 0,
|
|
939
|
-
cacheHits: 0,
|
|
940
|
-
cacheMisses: 0,
|
|
941
|
-
lastOperation: 0
|
|
942
|
-
};
|
|
943
|
-
metrics.totalOperations++;
|
|
944
|
-
metrics.lastOperation = Date.now();
|
|
945
|
-
if (cacheHit) {
|
|
946
|
-
metrics.cacheHits++;
|
|
947
|
-
} else {
|
|
948
|
-
metrics.cacheMisses++;
|
|
949
|
-
}
|
|
950
|
-
setSessionState({
|
|
951
|
-
...sessionState,
|
|
952
|
-
todoMetrics: metrics
|
|
953
|
-
});
|
|
954
|
-
}
|
|
955
|
-
function getTodos(agentId) {
|
|
956
|
-
const resolvedAgentId = resolveAgentId(agentId);
|
|
957
|
-
const now = Date.now();
|
|
958
|
-
if (agentId) {
|
|
959
|
-
updateMetrics("getTodos", false);
|
|
960
|
-
const agentTodos = readAgentData(resolvedAgentId) || [];
|
|
961
|
-
const agentCacheKey = `todoCache_${resolvedAgentId}`;
|
|
962
|
-
return agentTodos.map((todo) => ({
|
|
963
|
-
...todo,
|
|
964
|
-
activeForm: todo.activeForm || todo.content
|
|
965
|
-
}));
|
|
966
|
-
}
|
|
967
|
-
if (todoCache && now - cacheTimestamp < CACHE_TTL) {
|
|
968
|
-
updateMetrics("getTodos", true);
|
|
969
|
-
return todoCache.map((todo) => ({
|
|
970
|
-
...todo,
|
|
971
|
-
activeForm: todo.activeForm || todo.content
|
|
972
|
-
}));
|
|
973
|
-
}
|
|
974
|
-
updateMetrics("getTodos", false);
|
|
975
|
-
const sessionState = getSessionState();
|
|
976
|
-
const todos = sessionState[TODO_STORAGE_KEY] || [];
|
|
977
|
-
todoCache = [...todos].map((todo) => ({
|
|
978
|
-
...todo,
|
|
979
|
-
activeForm: todo.activeForm || todo.content
|
|
980
|
-
}));
|
|
981
|
-
cacheTimestamp = now;
|
|
982
|
-
return todoCache;
|
|
983
|
-
}
|
|
984
|
-
function setTodos(todos, agentId) {
|
|
985
|
-
const resolvedAgentId = resolveAgentId(agentId);
|
|
986
|
-
const config = getTodoConfig();
|
|
987
|
-
const existingTodos = getTodos(agentId);
|
|
988
|
-
if (agentId) {
|
|
989
|
-
if (todos.length > config.maxTodos) {
|
|
990
|
-
throw new Error(
|
|
991
|
-
`Todo limit exceeded. Maximum ${config.maxTodos} todos allowed.`
|
|
992
|
-
);
|
|
993
|
-
}
|
|
994
|
-
let processedTodos2 = todos;
|
|
995
|
-
if (config.autoArchiveCompleted) {
|
|
996
|
-
processedTodos2 = todos.filter((todo) => todo.status !== "completed");
|
|
997
|
-
}
|
|
998
|
-
const updatedTodos2 = processedTodos2.map((todo) => {
|
|
999
|
-
const existingTodo = existingTodos.find(
|
|
1000
|
-
(existing) => existing.id === todo.id
|
|
1001
|
-
);
|
|
1002
|
-
return {
|
|
1003
|
-
...todo,
|
|
1004
|
-
activeForm: todo.activeForm || todo.content,
|
|
1005
|
-
updatedAt: Date.now(),
|
|
1006
|
-
createdAt: todo.createdAt || Date.now(),
|
|
1007
|
-
previousStatus: existingTodo?.status !== todo.status ? existingTodo?.status : todo.previousStatus
|
|
1008
|
-
};
|
|
1009
|
-
});
|
|
1010
|
-
writeAgentData(resolvedAgentId, updatedTodos2);
|
|
1011
|
-
updateMetrics("setTodos");
|
|
1012
|
-
return;
|
|
1013
|
-
}
|
|
1014
|
-
if (todos.length > config.maxTodos) {
|
|
1015
|
-
throw new Error(
|
|
1016
|
-
`Todo limit exceeded. Maximum ${config.maxTodos} todos allowed.`
|
|
1017
|
-
);
|
|
1018
|
-
}
|
|
1019
|
-
let processedTodos = todos;
|
|
1020
|
-
if (config.autoArchiveCompleted) {
|
|
1021
|
-
processedTodos = todos.filter((todo) => todo.status !== "completed");
|
|
1022
|
-
}
|
|
1023
|
-
const updatedTodos = processedTodos.map((todo) => {
|
|
1024
|
-
const existingTodo = existingTodos.find((existing) => existing.id === todo.id);
|
|
1025
|
-
return {
|
|
1026
|
-
...todo,
|
|
1027
|
-
activeForm: todo.activeForm || todo.content,
|
|
1028
|
-
updatedAt: Date.now(),
|
|
1029
|
-
createdAt: todo.createdAt || Date.now(),
|
|
1030
|
-
previousStatus: existingTodo?.status !== todo.status ? existingTodo?.status : todo.previousStatus
|
|
1031
|
-
};
|
|
1032
|
-
});
|
|
1033
|
-
setSessionState({
|
|
1034
|
-
...getSessionState(),
|
|
1035
|
-
[TODO_STORAGE_KEY]: updatedTodos
|
|
1036
|
-
});
|
|
1037
|
-
invalidateCache();
|
|
1038
|
-
updateMetrics("setTodos");
|
|
1039
|
-
}
|
|
1040
|
-
function getTodoConfig() {
|
|
1041
|
-
const sessionState = getSessionState();
|
|
1042
|
-
return { ...DEFAULT_CONFIG, ...sessionState[TODO_CONFIG_KEY] || {} };
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
// src/services/system/systemReminder.ts
|
|
1046
|
-
var SystemReminderService = class {
|
|
1047
|
-
sessionState = {
|
|
1048
|
-
lastTodoUpdate: 0,
|
|
1049
|
-
lastFileAccess: 0,
|
|
1050
|
-
lastTaskUpdate: 0,
|
|
1051
|
-
sessionStartTime: Date.now(),
|
|
1052
|
-
remindersSent: /* @__PURE__ */ new Set(),
|
|
1053
|
-
contextPresent: false,
|
|
1054
|
-
reminderCount: 0,
|
|
1055
|
-
taskEventTimestamps: /* @__PURE__ */ new Map(),
|
|
1056
|
-
config: {
|
|
1057
|
-
todoEmptyReminder: true,
|
|
1058
|
-
securityReminder: true,
|
|
1059
|
-
performanceReminder: true,
|
|
1060
|
-
maxRemindersPerSession: 10
|
|
1061
|
-
}
|
|
1062
|
-
};
|
|
1063
|
-
eventDispatcher = /* @__PURE__ */ new Map();
|
|
1064
|
-
reminderCache = /* @__PURE__ */ new Map();
|
|
1065
|
-
constructor() {
|
|
1066
|
-
this.setupEventDispatcher();
|
|
1067
|
-
}
|
|
1068
|
-
generateReminders(hasContext = false, agentId) {
|
|
1069
|
-
this.sessionState.contextPresent = hasContext;
|
|
1070
|
-
if (!hasContext) {
|
|
1071
|
-
return [];
|
|
1072
|
-
}
|
|
1073
|
-
if (this.sessionState.reminderCount >= this.sessionState.config.maxRemindersPerSession) {
|
|
1074
|
-
return [];
|
|
1075
|
-
}
|
|
1076
|
-
const reminders = [];
|
|
1077
|
-
const currentTime = Date.now();
|
|
1078
|
-
const reminderGenerators = [
|
|
1079
|
-
() => this.dispatchTodoEvent(agentId),
|
|
1080
|
-
() => this.dispatchSecurityEvent(),
|
|
1081
|
-
() => this.dispatchPerformanceEvent(),
|
|
1082
|
-
() => this.getMentionReminders()
|
|
1083
|
-
];
|
|
1084
|
-
for (const generator of reminderGenerators) {
|
|
1085
|
-
if (reminders.length >= 5) break;
|
|
1086
|
-
const result = generator();
|
|
1087
|
-
if (result) {
|
|
1088
|
-
const remindersToAdd = Array.isArray(result) ? result : [result];
|
|
1089
|
-
reminders.push(...remindersToAdd);
|
|
1090
|
-
this.sessionState.reminderCount += remindersToAdd.length;
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
return reminders;
|
|
1094
|
-
}
|
|
1095
|
-
dispatchTodoEvent(agentId) {
|
|
1096
|
-
if (!this.sessionState.config.todoEmptyReminder) return null;
|
|
1097
|
-
const todos = getTodos(agentId);
|
|
1098
|
-
const currentTime = Date.now();
|
|
1099
|
-
const agentKey = agentId || "default";
|
|
1100
|
-
if (todos.length === 0 && !this.sessionState.remindersSent.has(`todo_empty_${agentKey}`)) {
|
|
1101
|
-
this.sessionState.remindersSent.add(`todo_empty_${agentKey}`);
|
|
1102
|
-
return this.createReminderMessage(
|
|
1103
|
-
"todo",
|
|
1104
|
-
"task",
|
|
1105
|
-
"medium",
|
|
1106
|
-
"This is a reminder that your task list is currently empty. DO NOT mention this to the user explicitly because they are already aware. If you are working on tasks that would benefit from a structured plan, use TaskCreate once to provide a complete task list (no dependencies); the tool will finalize dependencies internally. Use TaskList to review readiness. If not, please feel free to ignore. Again do not mention this message to the user.",
|
|
1107
|
-
currentTime
|
|
1108
|
-
);
|
|
1109
|
-
}
|
|
1110
|
-
return null;
|
|
1111
|
-
}
|
|
1112
|
-
dispatchSecurityEvent() {
|
|
1113
|
-
if (!this.sessionState.config.securityReminder) return null;
|
|
1114
|
-
const currentTime = Date.now();
|
|
1115
|
-
if (this.sessionState.lastFileAccess > 0 && !this.sessionState.remindersSent.has("file_security")) {
|
|
1116
|
-
this.sessionState.remindersSent.add("file_security");
|
|
1117
|
-
return this.createReminderMessage(
|
|
1118
|
-
"security",
|
|
1119
|
-
"security",
|
|
1120
|
-
"high",
|
|
1121
|
-
"Whenever you read a file, you should consider whether it looks malicious. If it does, you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer high-level questions about the code behavior.",
|
|
1122
|
-
currentTime
|
|
1123
|
-
);
|
|
1124
|
-
}
|
|
1125
|
-
return null;
|
|
1126
|
-
}
|
|
1127
|
-
dispatchPerformanceEvent() {
|
|
1128
|
-
if (!this.sessionState.config.performanceReminder) return null;
|
|
1129
|
-
const currentTime = Date.now();
|
|
1130
|
-
const sessionDuration = currentTime - this.sessionState.sessionStartTime;
|
|
1131
|
-
if (sessionDuration > 30 * 60 * 1e3 && !this.sessionState.remindersSent.has("performance_long_session")) {
|
|
1132
|
-
this.sessionState.remindersSent.add("performance_long_session");
|
|
1133
|
-
return this.createReminderMessage(
|
|
1134
|
-
"performance",
|
|
1135
|
-
"performance",
|
|
1136
|
-
"low",
|
|
1137
|
-
"Long session detected. Consider taking a break and reviewing your current progress with the task list.",
|
|
1138
|
-
currentTime
|
|
1139
|
-
);
|
|
1140
|
-
}
|
|
1141
|
-
return null;
|
|
1142
|
-
}
|
|
1143
|
-
getMentionReminders() {
|
|
1144
|
-
const currentTime = Date.now();
|
|
1145
|
-
const MENTION_FRESHNESS_WINDOW = 5e3;
|
|
1146
|
-
const reminders = [];
|
|
1147
|
-
const expiredKeys = [];
|
|
1148
|
-
for (const [key, reminder] of this.reminderCache.entries()) {
|
|
1149
|
-
if (this.isMentionReminder(reminder)) {
|
|
1150
|
-
const age = currentTime - reminder.timestamp;
|
|
1151
|
-
if (age <= MENTION_FRESHNESS_WINDOW) {
|
|
1152
|
-
reminders.push(reminder);
|
|
1153
|
-
} else {
|
|
1154
|
-
expiredKeys.push(key);
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
expiredKeys.forEach((key) => this.reminderCache.delete(key));
|
|
1159
|
-
return reminders;
|
|
1160
|
-
}
|
|
1161
|
-
isMentionReminder(reminder) {
|
|
1162
|
-
const mentionTypes = ["agent_mention", "file_mention", "ask_model_mention"];
|
|
1163
|
-
return mentionTypes.includes(reminder.type);
|
|
1164
|
-
}
|
|
1165
|
-
generateFileChangeReminder(context) {
|
|
1166
|
-
const { agentId, filePath, reminder } = context;
|
|
1167
|
-
if (!reminder) {
|
|
1168
|
-
return null;
|
|
1169
|
-
}
|
|
1170
|
-
const currentTime = Date.now();
|
|
1171
|
-
const reminderKey = `file_changed_${agentId}_${filePath}_${currentTime}`;
|
|
1172
|
-
if (this.sessionState.remindersSent.has(reminderKey)) {
|
|
1173
|
-
return null;
|
|
1174
|
-
}
|
|
1175
|
-
this.sessionState.remindersSent.add(reminderKey);
|
|
1176
|
-
return this.createReminderMessage(
|
|
1177
|
-
"file_changed",
|
|
1178
|
-
"general",
|
|
1179
|
-
"medium",
|
|
1180
|
-
reminder,
|
|
1181
|
-
currentTime
|
|
1182
|
-
);
|
|
1183
|
-
}
|
|
1184
|
-
createReminderMessage(type, category, priority, content, timestamp) {
|
|
1185
|
-
return {
|
|
1186
|
-
role: "system",
|
|
1187
|
-
content: `<system-reminder>
|
|
1188
|
-
${content}
|
|
1189
|
-
</system-reminder>`,
|
|
1190
|
-
isMeta: true,
|
|
1191
|
-
timestamp,
|
|
1192
|
-
type,
|
|
1193
|
-
priority,
|
|
1194
|
-
category
|
|
1195
|
-
};
|
|
1196
|
-
}
|
|
1197
|
-
getTodoStateHash(todos) {
|
|
1198
|
-
return todos.map((t) => `${t.content}:${t.status}:${t.activeForm || t.content}`).sort().join("|");
|
|
1199
|
-
}
|
|
1200
|
-
clearTodoReminders(agentId) {
|
|
1201
|
-
const agentKey = agentId || "default";
|
|
1202
|
-
for (const key of this.sessionState.remindersSent) {
|
|
1203
|
-
if (key.startsWith(`todo_updated_${agentKey}_`)) {
|
|
1204
|
-
this.sessionState.remindersSent.delete(key);
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
shouldEmitTaskReminder(key, throttleMs) {
|
|
1209
|
-
const now = Date.now();
|
|
1210
|
-
const last = this.sessionState.taskEventTimestamps.get(key) ?? 0;
|
|
1211
|
-
if (now - last < throttleMs) {
|
|
1212
|
-
return false;
|
|
1213
|
-
}
|
|
1214
|
-
this.sessionState.taskEventTimestamps.set(key, now);
|
|
1215
|
-
return true;
|
|
1216
|
-
}
|
|
1217
|
-
createTaskListReminder(context) {
|
|
1218
|
-
const { listId, summary } = context;
|
|
1219
|
-
if (!summary || !listId) return null;
|
|
1220
|
-
const reminderKey = `task_list_${listId}_${summary.total}_${summary.ready}_${summary.blocked}_${summary.inProgress}_${summary.pending}_${summary.done}_${summary.archived}`;
|
|
1221
|
-
if (this.sessionState.remindersSent.has(reminderKey)) return null;
|
|
1222
|
-
this.sessionState.remindersSent.add(reminderKey);
|
|
1223
|
-
return this.createReminderMessage(
|
|
1224
|
-
"task_list_changed",
|
|
1225
|
-
"task",
|
|
1226
|
-
"medium",
|
|
1227
|
-
`Your task list changed. Ready: ${summary.ready}, Blocked: ${summary.blocked}, In progress: ${summary.inProgress}, Pending: ${summary.pending}, Done: ${summary.done}, Archived: ${summary.archived}.`,
|
|
1228
|
-
Date.now()
|
|
1229
|
-
);
|
|
1230
|
-
}
|
|
1231
|
-
createTaskReadyReminder(context) {
|
|
1232
|
-
const { listId, readyIds } = context;
|
|
1233
|
-
if (!listId || !Array.isArray(readyIds)) return null;
|
|
1234
|
-
const limited = readyIds.slice(0, 5).join(", ");
|
|
1235
|
-
const reminderKey = `task_ready_${listId}_${readyIds.length}_${limited}`;
|
|
1236
|
-
if (this.sessionState.remindersSent.has(reminderKey)) return null;
|
|
1237
|
-
this.sessionState.remindersSent.add(reminderKey);
|
|
1238
|
-
return this.createReminderMessage(
|
|
1239
|
-
"task_ready_changed",
|
|
1240
|
-
"task",
|
|
1241
|
-
"medium",
|
|
1242
|
-
`Task readiness changed. Ready count: ${readyIds.length}. Ready ids: ${limited || "none"}.`,
|
|
1243
|
-
Date.now()
|
|
1244
|
-
);
|
|
1245
|
-
}
|
|
1246
|
-
createTaskConflictReminder(context) {
|
|
1247
|
-
const { listId, taskId } = context;
|
|
1248
|
-
if (!listId || !taskId) return null;
|
|
1249
|
-
const reminderKey = `task_conflict_${listId}_${taskId}`;
|
|
1250
|
-
if (this.sessionState.remindersSent.has(reminderKey)) return null;
|
|
1251
|
-
this.sessionState.remindersSent.add(reminderKey);
|
|
1252
|
-
return this.createReminderMessage(
|
|
1253
|
-
"task_conflict",
|
|
1254
|
-
"task",
|
|
1255
|
-
"high",
|
|
1256
|
-
`Task update conflict detected for ${taskId}. Refresh task data and retry updates with the latest baseVersion.`,
|
|
1257
|
-
Date.now()
|
|
1258
|
-
);
|
|
1259
|
-
}
|
|
1260
|
-
createTaskCreatedReminder(context) {
|
|
1261
|
-
const { listId, taskId, task } = context;
|
|
1262
|
-
if (!listId || !taskId) return null;
|
|
1263
|
-
const reminderKey = `task_created_${listId}_${taskId}`;
|
|
1264
|
-
if (this.sessionState.remindersSent.has(reminderKey)) return null;
|
|
1265
|
-
this.sessionState.remindersSent.add(reminderKey);
|
|
1266
|
-
const subject = task?.subject || taskId;
|
|
1267
|
-
return this.createReminderMessage(
|
|
1268
|
-
"task_created",
|
|
1269
|
-
"task",
|
|
1270
|
-
"medium",
|
|
1271
|
-
`Task created: ${subject} (${taskId}). Review details and adjust status if needed.`,
|
|
1272
|
-
Date.now()
|
|
1273
|
-
);
|
|
1274
|
-
}
|
|
1275
|
-
createTaskUpdatedReminder(context) {
|
|
1276
|
-
const { listId, taskId, task } = context;
|
|
1277
|
-
if (!listId || !taskId) return null;
|
|
1278
|
-
const reminderKey = `task_updated_${listId}_${taskId}_${task?.baseVersion ?? ""}`;
|
|
1279
|
-
if (this.sessionState.remindersSent.has(reminderKey)) return null;
|
|
1280
|
-
this.sessionState.remindersSent.add(reminderKey);
|
|
1281
|
-
const subject = task?.subject || taskId;
|
|
1282
|
-
return this.createReminderMessage(
|
|
1283
|
-
"task_updated",
|
|
1284
|
-
"task",
|
|
1285
|
-
"medium",
|
|
1286
|
-
`Task updated: ${subject} (${taskId}). Confirm dependencies and status are correct.`,
|
|
1287
|
-
Date.now()
|
|
1288
|
-
);
|
|
1289
|
-
}
|
|
1290
|
-
createTaskStatusReminder(context) {
|
|
1291
|
-
const { listId, taskId, previousStatus, status } = context;
|
|
1292
|
-
if (!listId || !taskId) return null;
|
|
1293
|
-
const reminderKey = `task_status_${listId}_${taskId}_${previousStatus}_${status}`;
|
|
1294
|
-
if (this.sessionState.remindersSent.has(reminderKey)) return null;
|
|
1295
|
-
this.sessionState.remindersSent.add(reminderKey);
|
|
1296
|
-
return this.createReminderMessage(
|
|
1297
|
-
"task_status_changed",
|
|
1298
|
-
"task",
|
|
1299
|
-
"medium",
|
|
1300
|
-
`Task status changed for ${taskId}: ${previousStatus} \u2192 ${status}. Review dependent tasks if needed.`,
|
|
1301
|
-
Date.now()
|
|
1302
|
-
);
|
|
1303
|
-
}
|
|
1304
|
-
createTaskDepsReminder(context) {
|
|
1305
|
-
const { listId, taskId, deps } = context;
|
|
1306
|
-
if (!listId || !taskId) return null;
|
|
1307
|
-
const reminderKey = `task_deps_${listId}_${taskId}_${JSON.stringify(deps ?? {})}`;
|
|
1308
|
-
if (this.sessionState.remindersSent.has(reminderKey)) return null;
|
|
1309
|
-
this.sessionState.remindersSent.add(reminderKey);
|
|
1310
|
-
return this.createReminderMessage(
|
|
1311
|
-
"task_deps_changed",
|
|
1312
|
-
"task",
|
|
1313
|
-
"medium",
|
|
1314
|
-
`Task dependencies changed for ${taskId}. Recheck readiness and blockers.`,
|
|
1315
|
-
Date.now()
|
|
1316
|
-
);
|
|
1317
|
-
}
|
|
1318
|
-
setupEventDispatcher() {
|
|
1319
|
-
this.addEventListener("session:startup", (context) => {
|
|
1320
|
-
this.resetSession();
|
|
1321
|
-
this.sessionState.sessionStartTime = Date.now();
|
|
1322
|
-
this.sessionState.contextPresent = Object.keys(context.context || {}).length > 0;
|
|
1323
|
-
});
|
|
1324
|
-
this.addEventListener("todo:changed", (context) => {
|
|
1325
|
-
this.sessionState.lastTodoUpdate = Date.now();
|
|
1326
|
-
this.clearTodoReminders(context.agentId);
|
|
1327
|
-
});
|
|
1328
|
-
this.addEventListener("todo:file_changed", (context) => {
|
|
1329
|
-
const agentId = context.agentId || "default";
|
|
1330
|
-
this.clearTodoReminders(agentId);
|
|
1331
|
-
this.sessionState.lastTodoUpdate = Date.now();
|
|
1332
|
-
const reminder = this.generateFileChangeReminder(context);
|
|
1333
|
-
if (reminder) {
|
|
1334
|
-
this.emitEvent("reminder:inject", {
|
|
1335
|
-
reminder: reminder.content,
|
|
1336
|
-
agentId,
|
|
1337
|
-
type: "file_changed",
|
|
1338
|
-
timestamp: Date.now()
|
|
1339
|
-
});
|
|
1340
|
-
}
|
|
1341
|
-
});
|
|
1342
|
-
this.addEventListener("task:list_changed", (context) => {
|
|
1343
|
-
const key = `task_list_${context.listId}`;
|
|
1344
|
-
if (!this.shouldEmitTaskReminder(key, 2e3)) return;
|
|
1345
|
-
this.sessionState.lastTaskUpdate = Date.now();
|
|
1346
|
-
const reminder = this.createTaskListReminder(context);
|
|
1347
|
-
if (reminder) {
|
|
1348
|
-
this.emitEvent("reminder:inject", {
|
|
1349
|
-
reminder: reminder.content,
|
|
1350
|
-
listId: context.listId,
|
|
1351
|
-
type: "task_list_changed",
|
|
1352
|
-
timestamp: Date.now()
|
|
1353
|
-
});
|
|
1354
|
-
}
|
|
1355
|
-
});
|
|
1356
|
-
this.addEventListener("task:ready_changed", (context) => {
|
|
1357
|
-
const key = `task_ready_${context.listId}`;
|
|
1358
|
-
if (!this.shouldEmitTaskReminder(key, 2e3)) return;
|
|
1359
|
-
const reminder = this.createTaskReadyReminder(context);
|
|
1360
|
-
if (reminder) {
|
|
1361
|
-
this.emitEvent("reminder:inject", {
|
|
1362
|
-
reminder: reminder.content,
|
|
1363
|
-
listId: context.listId,
|
|
1364
|
-
type: "task_ready_changed",
|
|
1365
|
-
timestamp: Date.now()
|
|
1366
|
-
});
|
|
1367
|
-
}
|
|
1368
|
-
});
|
|
1369
|
-
this.addEventListener("task:conflict_detected", (context) => {
|
|
1370
|
-
const key = `task_conflict_${context.listId}_${context.taskId}`;
|
|
1371
|
-
if (!this.shouldEmitTaskReminder(key, 2e3)) return;
|
|
1372
|
-
const reminder = this.createTaskConflictReminder(context);
|
|
1373
|
-
if (reminder) {
|
|
1374
|
-
this.emitEvent("reminder:inject", {
|
|
1375
|
-
reminder: reminder.content,
|
|
1376
|
-
listId: context.listId,
|
|
1377
|
-
type: "task_conflict",
|
|
1378
|
-
timestamp: Date.now()
|
|
1379
|
-
});
|
|
1380
|
-
}
|
|
1381
|
-
});
|
|
1382
|
-
this.addEventListener("task.created", (context) => {
|
|
1383
|
-
const key = `task_created_${context.listId}_${context.taskId}`;
|
|
1384
|
-
if (!this.shouldEmitTaskReminder(key, 2e3)) return;
|
|
1385
|
-
const reminder = this.createTaskCreatedReminder(context);
|
|
1386
|
-
if (reminder) {
|
|
1387
|
-
this.emitEvent("reminder:inject", {
|
|
1388
|
-
reminder: reminder.content,
|
|
1389
|
-
listId: context.listId,
|
|
1390
|
-
type: reminder.type,
|
|
1391
|
-
timestamp: reminder.timestamp
|
|
1392
|
-
});
|
|
1393
|
-
}
|
|
1394
|
-
});
|
|
1395
|
-
this.addEventListener("task.updated", (context) => {
|
|
1396
|
-
const key = `task_updated_${context.listId}_${context.taskId}`;
|
|
1397
|
-
if (!this.shouldEmitTaskReminder(key, 2e3)) return;
|
|
1398
|
-
const reminder = this.createTaskUpdatedReminder(context);
|
|
1399
|
-
if (reminder) {
|
|
1400
|
-
this.emitEvent("reminder:inject", {
|
|
1401
|
-
reminder: reminder.content,
|
|
1402
|
-
listId: context.listId,
|
|
1403
|
-
type: reminder.type,
|
|
1404
|
-
timestamp: reminder.timestamp
|
|
1405
|
-
});
|
|
1406
|
-
}
|
|
1407
|
-
});
|
|
1408
|
-
this.addEventListener("task.status_changed", (context) => {
|
|
1409
|
-
const key = `task_status_${context.listId}_${context.taskId}`;
|
|
1410
|
-
if (!this.shouldEmitTaskReminder(key, 2e3)) return;
|
|
1411
|
-
const reminder = this.createTaskStatusReminder(context);
|
|
1412
|
-
if (reminder) {
|
|
1413
|
-
this.emitEvent("reminder:inject", {
|
|
1414
|
-
reminder: reminder.content,
|
|
1415
|
-
listId: context.listId,
|
|
1416
|
-
type: reminder.type,
|
|
1417
|
-
timestamp: reminder.timestamp
|
|
1418
|
-
});
|
|
1419
|
-
}
|
|
1420
|
-
});
|
|
1421
|
-
this.addEventListener("task.deps_changed", (context) => {
|
|
1422
|
-
const key = `task_deps_${context.listId}_${context.taskId}`;
|
|
1423
|
-
if (!this.shouldEmitTaskReminder(key, 2e3)) return;
|
|
1424
|
-
const reminder = this.createTaskDepsReminder(context);
|
|
1425
|
-
if (reminder) {
|
|
1426
|
-
this.emitEvent("reminder:inject", {
|
|
1427
|
-
reminder: reminder.content,
|
|
1428
|
-
listId: context.listId,
|
|
1429
|
-
type: reminder.type,
|
|
1430
|
-
timestamp: reminder.timestamp
|
|
1431
|
-
});
|
|
1432
|
-
}
|
|
1433
|
-
});
|
|
1434
|
-
this.addEventListener("file:read", (context) => {
|
|
1435
|
-
this.sessionState.lastFileAccess = Date.now();
|
|
1436
|
-
});
|
|
1437
|
-
this.addEventListener("file:edited", (context) => {
|
|
1438
|
-
});
|
|
1439
|
-
this.addEventListener("agent:mentioned", (context) => {
|
|
1440
|
-
this.createMentionReminder({
|
|
1441
|
-
type: "agent_mention",
|
|
1442
|
-
key: `agent_mention_${context.agentType}_${context.timestamp}`,
|
|
1443
|
-
category: "task",
|
|
1444
|
-
priority: "high",
|
|
1445
|
-
content: `The user mentioned @${context.originalMention}. You MUST use the Task tool with subagent_type="${context.agentType}" to delegate this task to the specified agent. Provide a detailed, self-contained task description that fully captures the user's intent for the ${context.agentType} agent to execute.`,
|
|
1446
|
-
timestamp: context.timestamp
|
|
1447
|
-
});
|
|
1448
|
-
});
|
|
1449
|
-
this.addEventListener("file:mentioned", (context) => {
|
|
1450
|
-
this.createMentionReminder({
|
|
1451
|
-
type: "file_mention",
|
|
1452
|
-
key: `file_mention_${context.filePath}_${context.timestamp}`,
|
|
1453
|
-
category: "general",
|
|
1454
|
-
priority: "high",
|
|
1455
|
-
content: `The user mentioned @${context.originalMention}. You MUST read the entire content of the file at path: ${context.filePath} using the Read tool to understand the full context before proceeding with the user's request.`,
|
|
1456
|
-
timestamp: context.timestamp
|
|
1457
|
-
});
|
|
1458
|
-
});
|
|
1459
|
-
this.addEventListener("ask-model:mentioned", (context) => {
|
|
1460
|
-
this.createMentionReminder({
|
|
1461
|
-
type: "ask_model_mention",
|
|
1462
|
-
key: `ask_model_mention_${context.modelName}_${context.timestamp}`,
|
|
1463
|
-
category: "task",
|
|
1464
|
-
priority: "high",
|
|
1465
|
-
content: `The user mentioned @${context.modelName}. You MUST use the AskExpertModelTool to consult this specific model for expert opinions and analysis. Provide the user's question or context clearly to get the most relevant response from ${context.modelName}.`,
|
|
1466
|
-
timestamp: context.timestamp
|
|
1467
|
-
});
|
|
1468
|
-
});
|
|
1469
|
-
}
|
|
1470
|
-
addEventListener(event, callback) {
|
|
1471
|
-
if (!this.eventDispatcher.has(event)) {
|
|
1472
|
-
this.eventDispatcher.set(event, []);
|
|
1473
|
-
}
|
|
1474
|
-
this.eventDispatcher.get(event).push(callback);
|
|
1475
|
-
}
|
|
1476
|
-
emitEvent(event, context) {
|
|
1477
|
-
const listeners = this.eventDispatcher.get(event) || [];
|
|
1478
|
-
listeners.forEach((callback) => {
|
|
1479
|
-
try {
|
|
1480
|
-
callback(context);
|
|
1481
|
-
} catch (error) {
|
|
1482
|
-
logError(error);
|
|
1483
|
-
debug.warn("SYSTEM_REMINDER_LISTENER_ERROR", {
|
|
1484
|
-
event,
|
|
1485
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1486
|
-
});
|
|
1487
|
-
}
|
|
1488
|
-
});
|
|
1489
|
-
}
|
|
1490
|
-
createMentionReminder(params) {
|
|
1491
|
-
if (!this.sessionState.remindersSent.has(params.key)) {
|
|
1492
|
-
this.sessionState.remindersSent.add(params.key);
|
|
1493
|
-
const reminder = this.createReminderMessage(
|
|
1494
|
-
params.type,
|
|
1495
|
-
params.category,
|
|
1496
|
-
params.priority,
|
|
1497
|
-
params.content,
|
|
1498
|
-
params.timestamp
|
|
1499
|
-
);
|
|
1500
|
-
this.reminderCache.set(params.key, reminder);
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1503
|
-
resetSession() {
|
|
1504
|
-
this.sessionState = {
|
|
1505
|
-
lastTodoUpdate: 0,
|
|
1506
|
-
lastFileAccess: 0,
|
|
1507
|
-
lastTaskUpdate: 0,
|
|
1508
|
-
sessionStartTime: Date.now(),
|
|
1509
|
-
remindersSent: /* @__PURE__ */ new Set(),
|
|
1510
|
-
contextPresent: false,
|
|
1511
|
-
reminderCount: 0,
|
|
1512
|
-
taskEventTimestamps: /* @__PURE__ */ new Map(),
|
|
1513
|
-
config: { ...this.sessionState.config }
|
|
1514
|
-
};
|
|
1515
|
-
this.reminderCache.clear();
|
|
1516
|
-
}
|
|
1517
|
-
updateConfig(config) {
|
|
1518
|
-
this.sessionState.config = { ...this.sessionState.config, ...config };
|
|
1519
|
-
}
|
|
1520
|
-
getSessionState() {
|
|
1521
|
-
return { ...this.sessionState };
|
|
1522
|
-
}
|
|
1523
|
-
};
|
|
1524
|
-
var systemReminderService = new SystemReminderService();
|
|
1525
|
-
var generateSystemReminders = (hasContext = false, agentId) => systemReminderService.generateReminders(hasContext, agentId);
|
|
1526
|
-
var emitReminderEvent = (event, context) => systemReminderService.emitEvent(event, context);
|
|
1527
|
-
var resetReminderSession = () => systemReminderService.resetSession();
|
|
1528
|
-
|
|
1529
|
-
export {
|
|
1530
|
-
getAgentFilePath,
|
|
1531
|
-
generateAgentId,
|
|
1532
|
-
generateTaskListId,
|
|
1533
|
-
runWithTaskListId,
|
|
1534
|
-
runWithTaskListEnv,
|
|
1535
|
-
TaskStoreConflictError,
|
|
1536
|
-
getTaskListPaths,
|
|
1537
|
-
initTaskListLockFile,
|
|
1538
|
-
createTask,
|
|
1539
|
-
getTask,
|
|
1540
|
-
listTasks,
|
|
1541
|
-
updateTask,
|
|
1542
|
-
rebuildTaskBlocks,
|
|
1543
|
-
isTodoCompatEnabled,
|
|
1544
|
-
syncTodosToTasks,
|
|
1545
|
-
migrateTodosToTasks,
|
|
1546
|
-
getTodos,
|
|
1547
|
-
setTodos,
|
|
1548
|
-
systemReminderService,
|
|
1549
|
-
generateSystemReminders,
|
|
1550
|
-
emitReminderEvent,
|
|
1551
|
-
resetReminderSession
|
|
1552
|
-
};
|