aiden-runtime 4.1.0 → 4.1.2
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/README.md +89 -33
- package/dist/cli/v4/aidenCLI.js +162 -11
- package/dist/cli/v4/callbacks.js +5 -2
- package/dist/cli/v4/chatSession.js +525 -15
- package/dist/cli/v4/commands/auth.js +6 -3
- package/dist/cli/v4/commands/help.js +4 -0
- package/dist/cli/v4/commands/index.js +10 -1
- package/dist/cli/v4/commands/reloadSoul.js +37 -0
- package/dist/cli/v4/commands/update.js +102 -0
- package/dist/cli/v4/defaultSoul.js +68 -2
- package/dist/cli/v4/display.js +28 -10
- package/dist/cli/v4/doctor.js +173 -1
- package/dist/cli/v4/doctorLiveness.js +384 -0
- package/dist/cli/v4/promotionPrompt.js +202 -0
- package/dist/cli/v4/providerBootSelector.js +144 -0
- package/dist/cli/v4/sessionSummaryGate.js +66 -0
- package/dist/cli/v4/toolPreview.js +139 -0
- package/dist/core/v4/aidenAgent.js +91 -29
- package/dist/core/v4/capabilities.js +89 -0
- package/dist/core/v4/contextCompressor.js +25 -8
- package/dist/core/v4/distillationIndex.js +167 -0
- package/dist/core/v4/distillationStore.js +98 -0
- package/dist/core/v4/logger/logger.js +40 -9
- package/dist/core/v4/promotionCandidates.js +234 -0
- package/dist/core/v4/promptBuilder.js +145 -1
- package/dist/core/v4/sessionDistiller.js +405 -0
- package/dist/core/v4/skillMining/extractorPrompt.js +28 -21
- package/dist/core/v4/skillMining/proposalBuilder.js +3 -2
- package/dist/core/v4/skillMining/skillMiner.js +43 -6
- package/dist/core/v4/skillOutcomeTracker.js +323 -0
- package/dist/core/v4/subsystemHealth.js +143 -0
- package/dist/core/v4/update/executeInstall.js +233 -0
- package/dist/core/version.js +1 -1
- package/dist/moat/dangerousPatterns.js +1 -1
- package/dist/moat/memoryGuard.js +111 -0
- package/dist/moat/skillTeacher.js +14 -5
- package/dist/providers/v4/chatCompletionsAdapter.js +9 -0
- package/dist/providers/v4/codexResponsesAdapter.js +7 -2
- package/dist/providers/v4/errors.js +67 -1
- package/dist/providers/v4/modelDefaults.js +65 -0
- package/dist/providers/v4/ollamaPromptToolsAdapter.js +9 -2
- package/dist/providers/v4/registry.js +9 -2
- package/dist/providers/v4/runtimeResolver.js +6 -0
- package/dist/tools/v4/index.js +57 -1
- package/dist/tools/v4/memory/memoryRemove.js +57 -2
- package/dist/tools/v4/memory/sessionSummary.js +151 -0
- package/dist/tools/v4/sessions/recallSession.js +163 -0
- package/dist/tools/v4/sessions/sessionSearch.js +5 -1
- package/dist/tools/v4/subagent/subagentFanout.js +24 -0
- package/dist/tools/v4/system/_psHelpers.js +55 -0
- package/dist/tools/v4/system/aidenSelfUpdate.js +162 -0
- package/dist/tools/v4/system/appClose.js +79 -0
- package/dist/tools/v4/system/appLaunch.js +92 -0
- package/dist/tools/v4/system/clipboardRead.js +54 -0
- package/dist/tools/v4/system/clipboardWrite.js +84 -0
- package/dist/tools/v4/system/mediaKey.js +78 -0
- package/dist/tools/v4/system/osProcessList.js +99 -0
- package/dist/tools/v4/system/screenshot.js +106 -0
- package/dist/tools/v4/system/volumeSet.js +157 -0
- package/package.json +4 -1
- package/skills/system_control.md +135 -69
|
@@ -232,12 +232,19 @@ exports.PROVIDER_REGISTRY = {
|
|
|
232
232
|
apiMode: 'chat_completions',
|
|
233
233
|
baseUrl: 'https://api.deepseek.com/v1',
|
|
234
234
|
apiKeyEnvVar: 'DEEPSEEK_API_KEY',
|
|
235
|
-
description: 'DeepSeek direct API —
|
|
235
|
+
description: 'DeepSeek direct API — V4 Pro reasoning flagship + legacy aliases.',
|
|
236
236
|
tier: 'paid',
|
|
237
237
|
hasFreeTier: false,
|
|
238
238
|
docsUrl: 'https://api-docs.deepseek.com/',
|
|
239
239
|
supportsToolCalling: true,
|
|
240
|
-
|
|
240
|
+
// Phase v4.1.2-deepseek: `deepseek-v4-pro` prepended as the new
|
|
241
|
+
// flagship — becomes the auto-pick default for new users via
|
|
242
|
+
// pickProbeModel(). Legacy `deepseek-chat` and `deepseek-reasoner`
|
|
243
|
+
// retained for back-compat (still functional aliases of the V4
|
|
244
|
+
// flash family per DeepSeek docs; deprecated-but-live). Removal
|
|
245
|
+
// is its own deprecation slice. Per-call thinking + reasoning
|
|
246
|
+
// _effort defaults for v4-pro live in providers/v4/modelDefaults.ts.
|
|
247
|
+
modelIds: ['deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner'],
|
|
241
248
|
},
|
|
242
249
|
mistral: {
|
|
243
250
|
id: 'mistral',
|
|
@@ -39,6 +39,7 @@ const chatCompletionsAdapter_1 = require("./chatCompletionsAdapter");
|
|
|
39
39
|
const anthropicAdapter_1 = require("./anthropicAdapter");
|
|
40
40
|
const codexResponsesAdapter_1 = require("./codexResponsesAdapter");
|
|
41
41
|
const ollamaPromptToolsAdapter_1 = require("./ollamaPromptToolsAdapter");
|
|
42
|
+
const modelDefaults_1 = require("./modelDefaults");
|
|
42
43
|
const tokenStore_1 = require("../../core/v4/auth/tokenStore");
|
|
43
44
|
class RuntimeResolver {
|
|
44
45
|
constructor(credentialResolver) {
|
|
@@ -61,6 +62,11 @@ class RuntimeResolver {
|
|
|
61
62
|
model: model.id,
|
|
62
63
|
providerName: entry.id,
|
|
63
64
|
extraHeaders: entry.extraHeaders,
|
|
65
|
+
// Phase v4.1.2-deepseek: per-model body defaults (e.g.
|
|
66
|
+
// DeepSeek V4-Pro's mandatory thinking + reasoning_effort).
|
|
67
|
+
// Undefined for models without registered defaults — adapter
|
|
68
|
+
// skips the merge in that case.
|
|
69
|
+
defaultExtraBody: (0, modelDefaults_1.getModelDefaults)(entry.id, model.id)?.extraBody,
|
|
64
70
|
});
|
|
65
71
|
case 'anthropic_messages':
|
|
66
72
|
if (!credentials.apiKey) {
|
package/dist/tools/v4/index.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* Status: PHASE 8.
|
|
22
22
|
*/
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.memoryRemoveTool = exports.memoryReplaceTool = exports.memoryAddTool = exports.processWaitTool = exports.processKillTool = exports.processLogReadTool = exports.processListTool = exports.processSpawnTool = exports.executeCodeTool = exports.shellExecTool = exports.naturalEventsTool = exports.nowPlayingTool = exports.systemInfoTool = exports.makeLookupToolSchema = exports.skillManageTool = exports.skillViewTool = exports.skillsListTool = exports.sessionListTool = exports.sessionSearchTool = exports.browserCloseTool = exports.browserScrollTool = exports.browserFillTool = exports.browserTypeTool = exports.browserClickTool = exports.browserNavigateTool = exports.browserGetUrlTool = exports.browserExtractTool = exports.browserScreenshotTool = exports.fileCopyTool = exports.fileMoveTool = exports.fileDeleteTool = exports.filePatchTool = exports.fileWriteTool = exports.fileListTool = exports.fileReadTool = exports.deepResearchTool = exports.webPageTool = exports.webFetchTool = exports.webSearchTool = exports.makeSubagentFanoutTool = void 0;
|
|
24
|
+
exports.sessionSummaryTool = exports.memoryRemoveTool = exports.memoryReplaceTool = exports.memoryAddTool = exports.processWaitTool = exports.processKillTool = exports.processLogReadTool = exports.processListTool = exports.processSpawnTool = exports.executeCodeTool = exports.shellExecTool = exports.clipboardWriteTool = exports.clipboardReadTool = exports.appCloseTool = exports.appLaunchTool = exports.volumeSetTool = exports.mediaKeyTool = exports.osProcessListTool = exports.screenshotTool = exports.naturalEventsTool = exports.nowPlayingTool = exports.systemInfoTool = exports.makeLookupToolSchema = exports.skillManageTool = exports.skillViewTool = exports.skillsListTool = exports.sessionListTool = exports.sessionSearchTool = exports.browserCloseTool = exports.browserScrollTool = exports.browserFillTool = exports.browserTypeTool = exports.browserClickTool = exports.browserNavigateTool = exports.browserGetUrlTool = exports.browserExtractTool = exports.browserScreenshotTool = exports.fileCopyTool = exports.fileMoveTool = exports.fileDeleteTool = exports.filePatchTool = exports.fileWriteTool = exports.fileListTool = exports.fileReadTool = exports.deepResearchTool = exports.webPageTool = exports.webFetchTool = exports.webSearchTool = exports.makeSubagentFanoutTool = void 0;
|
|
25
25
|
exports.registerReadOnlyTools = registerReadOnlyTools;
|
|
26
26
|
exports.registerWriteTools = registerWriteTools;
|
|
27
27
|
exports.registerAllTools = registerAllTools;
|
|
@@ -49,6 +49,7 @@ const browserScroll_1 = require("./browser/browserScroll");
|
|
|
49
49
|
const browserClose_1 = require("./browser/browserClose");
|
|
50
50
|
const sessionSearch_1 = require("./sessions/sessionSearch");
|
|
51
51
|
const sessionList_1 = require("./sessions/sessionList");
|
|
52
|
+
const recallSession_1 = require("./sessions/recallSession");
|
|
52
53
|
const skillsList_1 = require("./skills/skillsList");
|
|
53
54
|
const skillView_1 = require("./skills/skillView");
|
|
54
55
|
const skillManage_1 = require("./skills/skillManage");
|
|
@@ -56,6 +57,18 @@ const lookupToolSchema_1 = require("./skills/lookupToolSchema");
|
|
|
56
57
|
const systemInfo_1 = require("./system/systemInfo");
|
|
57
58
|
const nowPlaying_1 = require("./system/nowPlaying");
|
|
58
59
|
const naturalEvents_1 = require("./system/naturalEvents");
|
|
60
|
+
// Phase v4.1.2-followup-3 computer-control bundle.
|
|
61
|
+
const screenshot_1 = require("./system/screenshot");
|
|
62
|
+
const osProcessList_1 = require("./system/osProcessList");
|
|
63
|
+
const mediaKey_1 = require("./system/mediaKey");
|
|
64
|
+
const volumeSet_1 = require("./system/volumeSet");
|
|
65
|
+
const appLaunch_1 = require("./system/appLaunch");
|
|
66
|
+
const appClose_1 = require("./system/appClose");
|
|
67
|
+
const clipboardRead_1 = require("./system/clipboardRead");
|
|
68
|
+
const clipboardWrite_1 = require("./system/clipboardWrite");
|
|
69
|
+
// Phase v4.1.2-update — natural-language self-update entry point.
|
|
70
|
+
// Routes through the same shared executeInstall executor as `/update install`.
|
|
71
|
+
const aidenSelfUpdate_1 = require("./system/aidenSelfUpdate");
|
|
59
72
|
const shellExec_1 = require("./terminal/shellExec");
|
|
60
73
|
const executeCode_1 = require("./executeCode");
|
|
61
74
|
const processSpawn_1 = require("./process/processSpawn");
|
|
@@ -66,6 +79,7 @@ const processWait_1 = require("./process/processWait");
|
|
|
66
79
|
const memoryAdd_1 = require("./memory/memoryAdd");
|
|
67
80
|
const memoryReplace_1 = require("./memory/memoryReplace");
|
|
68
81
|
const memoryRemove_1 = require("./memory/memoryRemove");
|
|
82
|
+
const sessionSummary_1 = require("./memory/sessionSummary");
|
|
69
83
|
const subagentFanout_1 = require("./subagent/subagentFanout");
|
|
70
84
|
/**
|
|
71
85
|
* Register every read-only tool into `registry`. The
|
|
@@ -94,11 +108,21 @@ function registerReadOnlyTools(registry) {
|
|
|
94
108
|
registry.register(browserGetUrl_1.browserGetUrlTool);
|
|
95
109
|
registry.register(sessionSearch_1.sessionSearchTool);
|
|
96
110
|
registry.register(sessionList_1.sessionListTool);
|
|
111
|
+
// Phase v4.1.2-memory-C: recall_session reads SessionDistillation
|
|
112
|
+
// files written by Phase A+B. Sits alongside session_search — the
|
|
113
|
+
// two have distinct purposes (FTS5-over-messages vs ranked
|
|
114
|
+
// distillation summaries); descriptions force the right model
|
|
115
|
+
// choice.
|
|
116
|
+
registry.register(recallSession_1.recallSessionTool);
|
|
97
117
|
registry.register(skillsList_1.skillsListTool);
|
|
98
118
|
registry.register(skillView_1.skillViewTool);
|
|
99
119
|
registry.register(systemInfo_1.systemInfoTool);
|
|
100
120
|
registry.register(nowPlaying_1.nowPlayingTool);
|
|
101
121
|
registry.register(naturalEvents_1.naturalEventsTool);
|
|
122
|
+
// Phase v4.1.2-followup-3 — computer-control read-only tools.
|
|
123
|
+
registry.register(screenshot_1.screenshotTool);
|
|
124
|
+
registry.register(osProcessList_1.osProcessListTool);
|
|
125
|
+
registry.register(clipboardRead_1.clipboardReadTool);
|
|
102
126
|
registry.register((0, lookupToolSchema_1.makeLookupToolSchema)(registry));
|
|
103
127
|
// Phase v4.1-subagent — register a stub for subagent_fanout so its
|
|
104
128
|
// schema is visible to the agent loop, the MCP server, and the
|
|
@@ -158,9 +182,22 @@ function registerWriteTools(registry) {
|
|
|
158
182
|
registry.register(memoryAdd_1.memoryAddTool);
|
|
159
183
|
registry.register(memoryReplace_1.memoryReplaceTool);
|
|
160
184
|
registry.register(memoryRemove_1.memoryRemoveTool);
|
|
185
|
+
// Phase v4.1.2 alive-core: cross-session continuity via /quit auto-summary.
|
|
186
|
+
registry.register(sessionSummary_1.sessionSummaryTool);
|
|
161
187
|
// Phase 10: skill_manage — mutating, also goes through the approval
|
|
162
188
|
// engine. skills_list / skill_view stay in registerReadOnlyTools.
|
|
163
189
|
registry.register(skillManage_1.skillManageTool);
|
|
190
|
+
// Phase v4.1.2-update: natural-language entry to the same install
|
|
191
|
+
// executor that /update install uses. Two-step confirmation gate
|
|
192
|
+
// (confirm:false → status; confirm:true → install).
|
|
193
|
+
registry.register(aidenSelfUpdate_1.aidenSelfUpdateTool);
|
|
194
|
+
// Phase v4.1.2-followup-3 — computer-control mutating tools. All
|
|
195
|
+
// route through the approval engine like every other write.
|
|
196
|
+
registry.register(mediaKey_1.mediaKeyTool);
|
|
197
|
+
registry.register(volumeSet_1.volumeSetTool);
|
|
198
|
+
registry.register(appLaunch_1.appLaunchTool);
|
|
199
|
+
registry.register(appClose_1.appCloseTool);
|
|
200
|
+
registry.register(clipboardWrite_1.clipboardWriteTool);
|
|
164
201
|
}
|
|
165
202
|
/** Register every v4 tool. Most callers want this. */
|
|
166
203
|
function registerAllTools(registry) {
|
|
@@ -227,6 +264,23 @@ var nowPlaying_2 = require("./system/nowPlaying");
|
|
|
227
264
|
Object.defineProperty(exports, "nowPlayingTool", { enumerable: true, get: function () { return nowPlaying_2.nowPlayingTool; } });
|
|
228
265
|
var naturalEvents_2 = require("./system/naturalEvents");
|
|
229
266
|
Object.defineProperty(exports, "naturalEventsTool", { enumerable: true, get: function () { return naturalEvents_2.naturalEventsTool; } });
|
|
267
|
+
// Phase v4.1.2-followup-3 computer-control bundle exports.
|
|
268
|
+
var screenshot_2 = require("./system/screenshot");
|
|
269
|
+
Object.defineProperty(exports, "screenshotTool", { enumerable: true, get: function () { return screenshot_2.screenshotTool; } });
|
|
270
|
+
var osProcessList_2 = require("./system/osProcessList");
|
|
271
|
+
Object.defineProperty(exports, "osProcessListTool", { enumerable: true, get: function () { return osProcessList_2.osProcessListTool; } });
|
|
272
|
+
var mediaKey_2 = require("./system/mediaKey");
|
|
273
|
+
Object.defineProperty(exports, "mediaKeyTool", { enumerable: true, get: function () { return mediaKey_2.mediaKeyTool; } });
|
|
274
|
+
var volumeSet_2 = require("./system/volumeSet");
|
|
275
|
+
Object.defineProperty(exports, "volumeSetTool", { enumerable: true, get: function () { return volumeSet_2.volumeSetTool; } });
|
|
276
|
+
var appLaunch_2 = require("./system/appLaunch");
|
|
277
|
+
Object.defineProperty(exports, "appLaunchTool", { enumerable: true, get: function () { return appLaunch_2.appLaunchTool; } });
|
|
278
|
+
var appClose_2 = require("./system/appClose");
|
|
279
|
+
Object.defineProperty(exports, "appCloseTool", { enumerable: true, get: function () { return appClose_2.appCloseTool; } });
|
|
280
|
+
var clipboardRead_2 = require("./system/clipboardRead");
|
|
281
|
+
Object.defineProperty(exports, "clipboardReadTool", { enumerable: true, get: function () { return clipboardRead_2.clipboardReadTool; } });
|
|
282
|
+
var clipboardWrite_2 = require("./system/clipboardWrite");
|
|
283
|
+
Object.defineProperty(exports, "clipboardWriteTool", { enumerable: true, get: function () { return clipboardWrite_2.clipboardWriteTool; } });
|
|
230
284
|
var shellExec_2 = require("./terminal/shellExec");
|
|
231
285
|
Object.defineProperty(exports, "shellExecTool", { enumerable: true, get: function () { return shellExec_2.shellExecTool; } });
|
|
232
286
|
var executeCode_2 = require("./executeCode");
|
|
@@ -247,3 +301,5 @@ var memoryReplace_2 = require("./memory/memoryReplace");
|
|
|
247
301
|
Object.defineProperty(exports, "memoryReplaceTool", { enumerable: true, get: function () { return memoryReplace_2.memoryReplaceTool; } });
|
|
248
302
|
var memoryRemove_2 = require("./memory/memoryRemove");
|
|
249
303
|
Object.defineProperty(exports, "memoryRemoveTool", { enumerable: true, get: function () { return memoryRemove_2.memoryRemoveTool; } });
|
|
304
|
+
var sessionSummary_2 = require("./memory/sessionSummary");
|
|
305
|
+
Object.defineProperty(exports, "sessionSummaryTool", { enumerable: true, get: function () { return sessionSummary_2.sessionSummaryTool; } });
|
|
@@ -12,14 +12,37 @@
|
|
|
12
12
|
* Returns `verified: true` only after the post-write read confirms
|
|
13
13
|
* the text is gone from the file.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
15
|
+
* Phase v4.1.2-bug-X: user-approved durable facts (anything in
|
|
16
|
+
* MEMORY.md `## Durable facts`) are protected from autonomous
|
|
17
|
+
* deletion. A subsequent session on a weak model (llama-3.3 in the
|
|
18
|
+
* smoke test) called memory_remove on a Phase-D-promoted fact with
|
|
19
|
+
* reasoning "outdated" — violating Phase D's opt-in trust contract.
|
|
20
|
+
*
|
|
21
|
+
* The guard is STRICT: if the requested substring appears ANYWHERE
|
|
22
|
+
* in the `## Durable facts` body, the call is rejected. Substring
|
|
23
|
+
* removal operates whole-file, so partial protection would still
|
|
24
|
+
* nuke the durable copy as side-effect. Hard rejection (vs propose-
|
|
25
|
+
* and-defer) is honest: model must surface to user; only the user
|
|
26
|
+
* (editing MEMORY.md directly, or via a future `/forget` slash
|
|
27
|
+
* command) can revoke durable content.
|
|
28
|
+
*
|
|
29
|
+
* Status: PHASE 9 (PHASE v4.1.2-bug-X: section protection).
|
|
16
30
|
*/
|
|
17
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
32
|
exports.memoryRemoveTool = void 0;
|
|
33
|
+
const memoryGuard_1 = require("../../../moat/memoryGuard");
|
|
34
|
+
/** Section in MEMORY.md that Phase D promotion writes to. */
|
|
35
|
+
const DURABLE_FACTS_HEADER = '## Durable facts';
|
|
19
36
|
exports.memoryRemoveTool = {
|
|
20
37
|
schema: {
|
|
21
38
|
name: 'memory_remove',
|
|
22
|
-
description: 'Remove an entry from MEMORY.md or USER.md by substring match.
|
|
39
|
+
description: 'Remove an entry from MEMORY.md or USER.md by substring match. ' +
|
|
40
|
+
'CANNOT remove entries in MEMORY.md `## Durable facts` — those are ' +
|
|
41
|
+
'user-approved facts the user explicitly promoted; only the user ' +
|
|
42
|
+
'can revoke them by editing MEMORY.md directly. If you think a ' +
|
|
43
|
+
'durable fact is outdated, surface that to the user and ask them ' +
|
|
44
|
+
'to confirm; do not propose autonomous removal. Returns ' +
|
|
45
|
+
'verified=true only after the change is confirmed on disk.',
|
|
23
46
|
inputSchema: {
|
|
24
47
|
type: 'object',
|
|
25
48
|
properties: {
|
|
@@ -42,6 +65,38 @@ exports.memoryRemoveTool = {
|
|
|
42
65
|
}
|
|
43
66
|
const file = args.file === 'user' ? 'user' : 'memory';
|
|
44
67
|
const text = String(args.text ?? '');
|
|
68
|
+
// Phase v4.1.2-bug-X: durable-section protection. Only applies
|
|
69
|
+
// to MEMORY.md (USER.md has no section structure today). The
|
|
70
|
+
// check requires snapshot access — when ctx.memory is not
|
|
71
|
+
// wired (test contexts, future surface refactors), we fall
|
|
72
|
+
// through to the existing guardedRemove behavior. Real CLI
|
|
73
|
+
// sessions wire memoryManager, so production paths get the
|
|
74
|
+
// guard. Documented intentional fall-through.
|
|
75
|
+
if (file === 'memory' && ctx.memory) {
|
|
76
|
+
try {
|
|
77
|
+
const snap = await ctx.memory.loadSnapshot();
|
|
78
|
+
const memoryMd = snap?.memoryMd ?? '';
|
|
79
|
+
if (memoryMd && (0, memoryGuard_1.containsInSection)(memoryMd, text, DURABLE_FACTS_HEADER)) {
|
|
80
|
+
const preview = text.length > 60
|
|
81
|
+
? `${text.slice(0, 60)}…`
|
|
82
|
+
: text;
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
verified: false,
|
|
86
|
+
error: `Cannot remove "${preview}" — it's in MEMORY.md ` +
|
|
87
|
+
`\`${DURABLE_FACTS_HEADER}\`, which holds user-approved facts. ` +
|
|
88
|
+
`Only the user can revoke these. Ask them to confirm removal ` +
|
|
89
|
+
`in their next message; do not propose autonomous deletion.`,
|
|
90
|
+
file,
|
|
91
|
+
protectedSection: DURABLE_FACTS_HEADER,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Snapshot read failed — fall through to old behavior rather
|
|
97
|
+
// than block legitimate removals on transient I/O errors.
|
|
98
|
+
}
|
|
99
|
+
}
|
|
45
100
|
const r = await ctx.memoryGuard.guardedRemove(file, text);
|
|
46
101
|
return {
|
|
47
102
|
success: r.ok,
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 Shiva Deore (Taracod).
|
|
4
|
+
* Licensed under AGPL-3.0. See LICENSE for details.
|
|
5
|
+
*
|
|
6
|
+
* Aiden — local-first agent.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* tools/v4/memory/sessionSummary.ts — Phase v4.1.2 alive-core.
|
|
10
|
+
*
|
|
11
|
+
* `session_summary` — append a five-bullet summary of the current
|
|
12
|
+
* session to MEMORY.md under a `## Recent sessions` section.
|
|
13
|
+
*
|
|
14
|
+
* Why this exists: until v4.1.2, "what did we work on last session?"
|
|
15
|
+
* was unanswerable — MEMORY.md held durable facts but no rolling
|
|
16
|
+
* conversation log. After this tool runs at /quit (or on demand), the
|
|
17
|
+
* NEXT session's PromptBuilder injects MEMORY.md as a slot, and the
|
|
18
|
+
* model can read the summary back as ambient context.
|
|
19
|
+
*
|
|
20
|
+
* Design:
|
|
21
|
+
*
|
|
22
|
+
* - The model is responsible for generating the five bullets. It
|
|
23
|
+
* already has the full conversation context in its message
|
|
24
|
+
* history, so this tool's job is *persistence* — not LLM dispatch.
|
|
25
|
+
* This avoids threading the AuxiliaryClient into ToolContext just
|
|
26
|
+
* for one tool and keeps the verify-on-disk contract clean.
|
|
27
|
+
*
|
|
28
|
+
* - Section rotation: append the new entry at the top of the
|
|
29
|
+
* section (most-recent-first), keep the most recent 10, drop the
|
|
30
|
+
* rest. Bound the size so MEMORY.md doesn't grow indefinitely.
|
|
31
|
+
*
|
|
32
|
+
* - Write goes through `MemoryGuard.replaceSection`, which preserves
|
|
33
|
+
* the standard `verified: true` contract that
|
|
34
|
+
* HonestyEnforcement relies on.
|
|
35
|
+
*/
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.sessionSummaryTool = void 0;
|
|
38
|
+
const RECENT_SESSIONS_HEADER = '## Recent sessions';
|
|
39
|
+
const MAX_RECENT_ENTRIES = 10;
|
|
40
|
+
/**
|
|
41
|
+
* Render one summary entry: timestamp header + bullets. Trims and
|
|
42
|
+
* normalises so two adjacent entries don't collide on whitespace.
|
|
43
|
+
*/
|
|
44
|
+
function formatEntry(bullets, when) {
|
|
45
|
+
const stamp = when.toISOString().replace(/\.\d+Z$/, 'Z'); // second precision
|
|
46
|
+
const cleaned = bullets
|
|
47
|
+
.map((b) => b.trim())
|
|
48
|
+
.filter((b) => b.length > 0)
|
|
49
|
+
.map((b) => (b.startsWith('-') ? b : `- ${b}`));
|
|
50
|
+
return [`### ${stamp}`, ...cleaned].join('\n');
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Split an existing Recent-sessions body into entries (each headed by
|
|
54
|
+
* a `### ` timestamp line). The most-recent entry is index 0.
|
|
55
|
+
*/
|
|
56
|
+
function parseEntries(body) {
|
|
57
|
+
const trimmed = body.trim();
|
|
58
|
+
if (!trimmed)
|
|
59
|
+
return [];
|
|
60
|
+
// Split on `### ` at line start; first chunk is empty when the body
|
|
61
|
+
// starts with the marker, which we filter out.
|
|
62
|
+
const parts = trimmed
|
|
63
|
+
.split(/^### /m)
|
|
64
|
+
.map((p) => p.trim())
|
|
65
|
+
.filter((p) => p.length > 0)
|
|
66
|
+
.map((p) => `### ${p}`);
|
|
67
|
+
return parts;
|
|
68
|
+
}
|
|
69
|
+
exports.sessionSummaryTool = {
|
|
70
|
+
schema: {
|
|
71
|
+
name: 'session_summary',
|
|
72
|
+
description: 'Append a five-bullet summary of the current session to MEMORY.md ' +
|
|
73
|
+
'(under "## Recent sessions"). The next session will see it as ambient ' +
|
|
74
|
+
'context. Call this at the end of a meaningful session, or right before ' +
|
|
75
|
+
'the user types /quit. You craft the bullets — be concise and concrete: ' +
|
|
76
|
+
'what we worked on, decisions made, files changed, problems solved, open items.',
|
|
77
|
+
inputSchema: {
|
|
78
|
+
type: 'object',
|
|
79
|
+
properties: {
|
|
80
|
+
bullets: {
|
|
81
|
+
type: 'array',
|
|
82
|
+
description: 'Exactly five concise bullets (3-15 words each) summarising the session. ' +
|
|
83
|
+
'Focus on what will be useful for the next session.',
|
|
84
|
+
items: {
|
|
85
|
+
type: 'string',
|
|
86
|
+
description: 'One bullet. Plain prose; leading "- " optional (added if missing).',
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
trigger: {
|
|
90
|
+
type: 'string',
|
|
91
|
+
enum: ['manual', 'auto-quit'],
|
|
92
|
+
description: 'Diagnostic only — whether the model invoked this directly ' +
|
|
93
|
+
'("manual") or the REPL auto-triggered it on /quit ("auto-quit"). ' +
|
|
94
|
+
'Defaults to "manual" when omitted.',
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
required: ['bullets'],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
category: 'write',
|
|
101
|
+
mutates: true,
|
|
102
|
+
toolset: 'memory',
|
|
103
|
+
async execute(args, ctx) {
|
|
104
|
+
if (!ctx.memoryGuard) {
|
|
105
|
+
return { success: false, error: 'memory guard not configured' };
|
|
106
|
+
}
|
|
107
|
+
if (!ctx.memory) {
|
|
108
|
+
return { success: false, error: 'memory manager not configured' };
|
|
109
|
+
}
|
|
110
|
+
const rawBullets = Array.isArray(args.bullets) ? args.bullets : [];
|
|
111
|
+
const bullets = rawBullets
|
|
112
|
+
.filter((b) => typeof b === 'string')
|
|
113
|
+
.map((b) => b.trim())
|
|
114
|
+
.filter((b) => b.length > 0);
|
|
115
|
+
if (bullets.length === 0) {
|
|
116
|
+
return {
|
|
117
|
+
success: false,
|
|
118
|
+
error: 'session_summary requires at least one non-empty bullet',
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const now = new Date();
|
|
122
|
+
const newEntry = formatEntry(bullets, now);
|
|
123
|
+
// Read current MEMORY.md to find existing Recent-sessions body.
|
|
124
|
+
const snap = await ctx.memory.loadSnapshot();
|
|
125
|
+
const memoryMd = snap.memoryMd ?? '';
|
|
126
|
+
// Pull existing section body (if any). The header consumes its
|
|
127
|
+
// line; the capture group grabs everything until the next h2 or
|
|
128
|
+
// EOF. Whitespace-only bodies are captured as empty after trim.
|
|
129
|
+
const headerEscaped = RECENT_SESSIONS_HEADER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
130
|
+
// Note: NO `m` flag — we want `$` to mean end-of-string, not
|
|
131
|
+
// end-of-line. With `m`, the lookahead `$` matches before every
|
|
132
|
+
// newline and we capture only the first body line instead of the
|
|
133
|
+
// whole section.
|
|
134
|
+
const sectionRe = new RegExp(`${headerEscaped}[^\\n]*\\n([\\s\\S]*?)(?=\\n## |$)`);
|
|
135
|
+
const match = memoryMd.match(sectionRe);
|
|
136
|
+
const existingBody = match ? (match[1] ?? '').trim() : '';
|
|
137
|
+
const existingEntries = parseEntries(existingBody);
|
|
138
|
+
// Most-recent-first ordering, capped to 10.
|
|
139
|
+
const combined = [newEntry, ...existingEntries].slice(0, MAX_RECENT_ENTRIES);
|
|
140
|
+
const newBody = combined.join('\n\n');
|
|
141
|
+
const result = await ctx.memoryGuard.replaceSection('memory', RECENT_SESSIONS_HEADER, newBody);
|
|
142
|
+
return {
|
|
143
|
+
success: result.ok,
|
|
144
|
+
verified: result.verified,
|
|
145
|
+
error: result.ok ? undefined : result.reason,
|
|
146
|
+
entries: combined.length,
|
|
147
|
+
trigger: args.trigger ?? 'manual',
|
|
148
|
+
timestamp: now.toISOString(),
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 Shiva Deore (Taracod).
|
|
4
|
+
* Licensed under AGPL-3.0. See LICENSE for details.
|
|
5
|
+
*
|
|
6
|
+
* Aiden — local-first agent.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* tools/v4/sessions/recallSession.ts — Phase v4.1.2-memory-C.
|
|
10
|
+
*
|
|
11
|
+
* `recall_session` — return ranked SessionDistillation summaries for
|
|
12
|
+
* past sessions matching the user's query (or just the most recent N
|
|
13
|
+
* when no query is supplied).
|
|
14
|
+
*
|
|
15
|
+
* Coexists with `session_search`:
|
|
16
|
+
* - session_search → FTS5 over message TEXT in SessionStore.
|
|
17
|
+
* Returns per-message snippets. Use when the user wants the exact
|
|
18
|
+
* words of a past message.
|
|
19
|
+
* - recall_session → ranked DISTILLATIONS by TOPIC. Returns
|
|
20
|
+
* structured per-session summaries (decisions, open items, files
|
|
21
|
+
* touched). Use when the user wants context on what HAPPENED in
|
|
22
|
+
* past sessions.
|
|
23
|
+
*
|
|
24
|
+
* Index strategy: scan-all. Reads every distillation JSON from
|
|
25
|
+
* `<paths.root>/distillations/` per query. Expected file count is
|
|
26
|
+
* <1000 per user; sub-100ms at that scale. When telemetry shows
|
|
27
|
+
* latency >500ms, the escalation path is direct migration to SQLite
|
|
28
|
+
* FTS5 — JSON-index intermediate is intentionally skipped.
|
|
29
|
+
*/
|
|
30
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
31
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
32
|
+
};
|
|
33
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
+
exports.__testFs = exports.recallSessionTool = void 0;
|
|
35
|
+
exports.getDistillationsDir = getDistillationsDir;
|
|
36
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
37
|
+
const node_fs_1 = require("node:fs");
|
|
38
|
+
const distillationStore_1 = require("../../../core/v4/distillationStore");
|
|
39
|
+
const distillationIndex_1 = require("../../../core/v4/distillationIndex");
|
|
40
|
+
const DEFAULT_LIMIT = 5;
|
|
41
|
+
const MAX_LIMIT = 25;
|
|
42
|
+
exports.recallSessionTool = {
|
|
43
|
+
schema: {
|
|
44
|
+
name: 'recall_session',
|
|
45
|
+
description: 'Recall past SESSIONS by topic. Returns ranked summaries — ' +
|
|
46
|
+
'decisions made, files touched, open items, tool usage — from ' +
|
|
47
|
+
'previously persisted session distillations. ' +
|
|
48
|
+
'For the EXACT WORDS of a past message, call `session_search` ' +
|
|
49
|
+
'instead (FTS5 over message text). For "what happened" / "what ' +
|
|
50
|
+
'did we work on" / "what was unfinished", use this tool.',
|
|
51
|
+
inputSchema: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
properties: {
|
|
54
|
+
query: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
description: 'Optional keyword filter. Case-insensitive substring match ' +
|
|
57
|
+
'across keywords, bullets, decisions, open_items, and ' +
|
|
58
|
+
'tool names. Omit to get the most recent sessions.',
|
|
59
|
+
},
|
|
60
|
+
limit: {
|
|
61
|
+
type: 'number',
|
|
62
|
+
description: `Maximum number of matches to return. Default ${DEFAULT_LIMIT}, max ${MAX_LIMIT}.`,
|
|
63
|
+
},
|
|
64
|
+
days: {
|
|
65
|
+
type: 'number',
|
|
66
|
+
description: 'Optional recency window in days. Drops distillations older ' +
|
|
67
|
+
'than this before ranking. Omit for no time filter.',
|
|
68
|
+
},
|
|
69
|
+
include_full: {
|
|
70
|
+
type: 'boolean',
|
|
71
|
+
description: 'When true, each match also carries tools_used + keywords ' +
|
|
72
|
+
'(useful when the agent needs granular tool history). ' +
|
|
73
|
+
'Default false to keep responses compact.',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
category: 'read',
|
|
79
|
+
mutates: false,
|
|
80
|
+
toolset: 'sessions',
|
|
81
|
+
async execute(args, ctx) {
|
|
82
|
+
if (!ctx.paths?.root) {
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
error: 'recall_session requires resolved aiden paths',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const dir = node_path_1.default.join(ctx.paths.root, 'distillations');
|
|
89
|
+
// Read everything off disk first. Each failure (malformed JSON,
|
|
90
|
+
// EACCES on individual files) is skipped silently; the diagnostic
|
|
91
|
+
// for "files exist but couldn't be read" is the gap between
|
|
92
|
+
// scanned (id count) and dists.length (parse-success count).
|
|
93
|
+
let ids;
|
|
94
|
+
try {
|
|
95
|
+
ids = await (0, distillationStore_1.listDistillationIds)(dir);
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
const code = err.code;
|
|
99
|
+
if (code === 'ENOENT') {
|
|
100
|
+
// No distillations directory yet — first-run case is
|
|
101
|
+
// success with zero matches, NOT a failure.
|
|
102
|
+
return {
|
|
103
|
+
success: true,
|
|
104
|
+
query: typeof args.query === 'string' ? args.query : undefined,
|
|
105
|
+
matches: [],
|
|
106
|
+
total_found: 0,
|
|
107
|
+
scanned: 0,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
success: false,
|
|
112
|
+
error: `Failed to enumerate distillations: ${err.message}`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const dists = [];
|
|
116
|
+
for (const id of ids) {
|
|
117
|
+
try {
|
|
118
|
+
const d = await (0, distillationStore_1.readDistillation)(dir, id);
|
|
119
|
+
if (d)
|
|
120
|
+
dists.push(d);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// One bad file shouldn't prevent the agent from seeing the
|
|
124
|
+
// rest. The user can diagnose via `aiden doctor` (the file
|
|
125
|
+
// is still on disk).
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const recallQuery = {
|
|
129
|
+
query: typeof args.query === 'string' ? args.query : undefined,
|
|
130
|
+
limit: typeof args.limit === 'number' ? args.limit : undefined,
|
|
131
|
+
days: typeof args.days === 'number' ? args.days : undefined,
|
|
132
|
+
include_full: args.include_full === true,
|
|
133
|
+
};
|
|
134
|
+
const ranked = (0, distillationIndex_1.rankDistillations)(dists, recallQuery);
|
|
135
|
+
return {
|
|
136
|
+
success: true,
|
|
137
|
+
query: recallQuery.query,
|
|
138
|
+
matches: ranked.matches,
|
|
139
|
+
total_found: ranked.total_found,
|
|
140
|
+
// scanned reflects files we attempted to read — useful diagnostic.
|
|
141
|
+
// If scanned > ranked.total_found AND dists.length < scanned, the
|
|
142
|
+
// delta is malformed files; the agent can suggest running aiden
|
|
143
|
+
// doctor to inspect.
|
|
144
|
+
scanned: ids.length,
|
|
145
|
+
};
|
|
146
|
+
// Note re: subsystem health — wire-up happens at the runtime
|
|
147
|
+
// construction layer (cli/v4/aidenCLI.ts) where the registry is
|
|
148
|
+
// built. The tool itself stays pure of registry-knowledge for
|
|
149
|
+
// testability; the registry caller decides whether to wrap the
|
|
150
|
+
// file-read errors in a tracker.
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
// Expose the directory path for runtime wire-up. Tools that want to
|
|
154
|
+
// register recall_session with the slice3 SubsystemHealthRegistry
|
|
155
|
+
// pass this helper to the tracker so they get health snapshots without
|
|
156
|
+
// hard-coding the path in two places.
|
|
157
|
+
function getDistillationsDir(rootDir) {
|
|
158
|
+
return node_path_1.default.join(rootDir, 'distillations');
|
|
159
|
+
}
|
|
160
|
+
// Re-export read-only fs surface used by tests under controlled
|
|
161
|
+
// fixtures. Production code never imports from here; the tool calls
|
|
162
|
+
// fs directly.
|
|
163
|
+
exports.__testFs = node_fs_1.promises;
|
|
@@ -22,7 +22,11 @@ const MAX_LIMIT = 50;
|
|
|
22
22
|
exports.sessionSearchTool = {
|
|
23
23
|
schema: {
|
|
24
24
|
name: 'session_search',
|
|
25
|
-
description: 'Search past conversation
|
|
25
|
+
description: 'Search past conversation MESSAGES by keyword (FTS5 full-text). ' +
|
|
26
|
+
'Returns matching message SNIPPETS with the session id and timestamp. ' +
|
|
27
|
+
'Use when you need the exact words a past message contained. ' +
|
|
28
|
+
'For topic-level recall of what happened in past sessions (decisions, ' +
|
|
29
|
+
'files touched, open items), call `recall_session` instead.',
|
|
26
30
|
inputSchema: {
|
|
27
31
|
type: 'object',
|
|
28
32
|
properties: {
|
|
@@ -73,6 +73,30 @@ function makeSubagentFanoutTool(factory) {
|
|
|
73
73
|
tasks: {
|
|
74
74
|
type: 'array',
|
|
75
75
|
description: 'Per-child task list (partition mode only). Length must equal n.',
|
|
76
|
+
// Schema mirrors PartitionTask interface in
|
|
77
|
+
// core/v4/subagent/fanout.ts:70-75. If you change one, change
|
|
78
|
+
// the other. OpenAI Codex backend strictly validates schemas
|
|
79
|
+
// and rejects `type: "array"` declarations missing `items`,
|
|
80
|
+
// so the inner shape must be explicit here.
|
|
81
|
+
items: {
|
|
82
|
+
type: 'object',
|
|
83
|
+
description: 'One unit of work for a partition-mode child.',
|
|
84
|
+
properties: {
|
|
85
|
+
goal: {
|
|
86
|
+
type: 'string',
|
|
87
|
+
description: 'The task this child should accomplish.',
|
|
88
|
+
},
|
|
89
|
+
context: {
|
|
90
|
+
type: 'string',
|
|
91
|
+
description: 'Optional shared context for the child.',
|
|
92
|
+
},
|
|
93
|
+
role: {
|
|
94
|
+
type: 'string',
|
|
95
|
+
description: 'Optional role tag, diagnostic only.',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
required: ['goal'],
|
|
99
|
+
},
|
|
76
100
|
},
|
|
77
101
|
merge: {
|
|
78
102
|
type: 'string',
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2026 Shiva Deore (Taracod).
|
|
4
|
+
* Licensed under AGPL-3.0. See LICENSE for details.
|
|
5
|
+
*
|
|
6
|
+
* Aiden — local-first agent.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* tools/v4/system/_psHelpers.ts — Phase v4.1.2-followup-3.
|
|
10
|
+
*
|
|
11
|
+
* Shared utilities for the computer-control tool family. Each tool
|
|
12
|
+
* (screenshot / os_process_list / media_key / volume_set / app_launch /
|
|
13
|
+
* app_close / clipboard_read / clipboard_write) gates on `win32` and
|
|
14
|
+
* shells out to PowerShell. The gate + exec boilerplate is identical
|
|
15
|
+
* across all eight tools — extracted here so the per-tool files stay
|
|
16
|
+
* focused on the one PowerShell snippet that matters.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.isWindows = exports.execAsync = void 0;
|
|
20
|
+
exports.windowsOnlyError = windowsOnlyError;
|
|
21
|
+
exports.runPowerShell = runPowerShell;
|
|
22
|
+
const node_child_process_1 = require("node:child_process");
|
|
23
|
+
const node_util_1 = require("node:util");
|
|
24
|
+
exports.execAsync = (0, node_util_1.promisify)(node_child_process_1.exec);
|
|
25
|
+
/**
|
|
26
|
+
* Standard "not supported on this platform" error payload. Surfaces a
|
|
27
|
+
* link the user can file an issue against rather than pretending the
|
|
28
|
+
* call quietly no-op'd.
|
|
29
|
+
*/
|
|
30
|
+
function windowsOnlyError(toolName) {
|
|
31
|
+
return {
|
|
32
|
+
success: false,
|
|
33
|
+
error: `Tool '${toolName}' is Windows-only in v4.1.2. macOS/Linux ` +
|
|
34
|
+
`support tracked at github.com/taracodlabs/aiden — please file an ` +
|
|
35
|
+
`issue if needed. (Detected platform: ${process.platform})`,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Run a PowerShell snippet and return stdout. Defaults to a 15-second
|
|
40
|
+
* timeout — caller passes a different one when a slower operation
|
|
41
|
+
* (screenshot, app launch) is expected.
|
|
42
|
+
*
|
|
43
|
+
* Single source of truth for the `shell: 'powershell.exe'` invocation
|
|
44
|
+
* shape so future powershell-CLI / `pwsh` migration is one-line.
|
|
45
|
+
*/
|
|
46
|
+
async function runPowerShell(script, options = {}) {
|
|
47
|
+
const opts = {
|
|
48
|
+
shell: 'powershell.exe',
|
|
49
|
+
timeout: options.timeoutMs ?? 15000,
|
|
50
|
+
maxBuffer: (options.maxBufferMb ?? 4) * 1024 * 1024,
|
|
51
|
+
};
|
|
52
|
+
return await (0, exports.execAsync)(script, opts);
|
|
53
|
+
}
|
|
54
|
+
const isWindows = () => process.platform === 'win32';
|
|
55
|
+
exports.isWindows = isWindows;
|