@hasna/assistants 0.6.21 → 0.6.23
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/.assistants/ASSISTANTS.md +10 -0
- package/dist/.assistants/skills/calendar/SKILL.md +40 -0
- package/dist/.assistants/skills/email/SKILL.md +41 -0
- package/dist/.assistants/skills/notes/SKILL.md +44 -0
- package/dist/.assistants/skills/search/SKILL.md +23 -0
- package/dist/.assistants/skills/skill-brainstorm/SKILL.md +30 -0
- package/dist/.assistants/skills/skill-draft/SKILL.md +30 -0
- package/dist/.assistants/skills/skill-research/SKILL.md +27 -0
- package/dist/.assistants/skills/skill-summarize/SKILL.md +25 -0
- package/dist/index.js +1296 -465
- package/dist/index.js.map +25 -22
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -27290,16 +27290,16 @@ var exports_anthropic = {};
|
|
|
27290
27290
|
__export(exports_anthropic, {
|
|
27291
27291
|
AnthropicClient: () => AnthropicClient
|
|
27292
27292
|
});
|
|
27293
|
-
import { readFileSync as
|
|
27293
|
+
import { readFileSync as readFileSync4, existsSync as existsSync8 } from "fs";
|
|
27294
27294
|
import { homedir as homedir8 } from "os";
|
|
27295
|
-
import { join as
|
|
27295
|
+
import { join as join13 } from "path";
|
|
27296
27296
|
function loadApiKeyFromSecrets() {
|
|
27297
27297
|
const envHome = process.env.HOME || process.env.USERPROFILE;
|
|
27298
27298
|
const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir8();
|
|
27299
|
-
const secretsPath =
|
|
27299
|
+
const secretsPath = join13(homeDir, ".secrets");
|
|
27300
27300
|
if (existsSync8(secretsPath)) {
|
|
27301
27301
|
try {
|
|
27302
|
-
const content =
|
|
27302
|
+
const content = readFileSync4(secretsPath, "utf-8");
|
|
27303
27303
|
const match = content.match(/export\s+ANTHROPIC_API_KEY\s*=\s*["']?([^"'\n]+)["']?/);
|
|
27304
27304
|
if (match) {
|
|
27305
27305
|
return match[1];
|
|
@@ -35505,9 +35505,12 @@ var import_react19 = __toESM(require_react(), 1);
|
|
|
35505
35505
|
var import_react20 = __toESM(require_react(), 1);
|
|
35506
35506
|
// node_modules/.bun/ink@5.2.1+f4eacebf2041cd4f/node_modules/ink/build/hooks/use-focus-manager.js
|
|
35507
35507
|
var import_react21 = __toESM(require_react(), 1);
|
|
35508
|
+
// packages/terminal/src/components/App.tsx
|
|
35509
|
+
var import_react29 = __toESM(require_react(), 1);
|
|
35510
|
+
|
|
35508
35511
|
// packages/core/src/agent/loop.ts
|
|
35509
35512
|
init_src();
|
|
35510
|
-
import { join as
|
|
35513
|
+
import { join as join20 } from "path";
|
|
35511
35514
|
|
|
35512
35515
|
// packages/core/src/agent/context.ts
|
|
35513
35516
|
init_src();
|
|
@@ -35515,6 +35518,7 @@ init_src();
|
|
|
35515
35518
|
class AgentContext {
|
|
35516
35519
|
messages = [];
|
|
35517
35520
|
maxMessages;
|
|
35521
|
+
scopeContext = null;
|
|
35518
35522
|
constructor(maxMessages = 100) {
|
|
35519
35523
|
this.maxMessages = maxMessages;
|
|
35520
35524
|
}
|
|
@@ -35610,6 +35614,15 @@ class AgentContext {
|
|
|
35610
35614
|
import(messages) {
|
|
35611
35615
|
this.messages = messages;
|
|
35612
35616
|
}
|
|
35617
|
+
setScopeContext(scope) {
|
|
35618
|
+
this.scopeContext = scope;
|
|
35619
|
+
}
|
|
35620
|
+
getScopeContext() {
|
|
35621
|
+
return this.scopeContext;
|
|
35622
|
+
}
|
|
35623
|
+
clearScopeContext() {
|
|
35624
|
+
this.scopeContext = null;
|
|
35625
|
+
}
|
|
35613
35626
|
}
|
|
35614
35627
|
|
|
35615
35628
|
// packages/core/src/context/manager.ts
|
|
@@ -36140,7 +36153,7 @@ import { dirname, join as join2 } from "path";
|
|
|
36140
36153
|
// packages/core/src/config.ts
|
|
36141
36154
|
import { join } from "path";
|
|
36142
36155
|
import { homedir } from "os";
|
|
36143
|
-
var DEFAULT_SYSTEM_PROMPT = `You are a helpful AI assistant running in the terminal.
|
|
36156
|
+
var DEFAULT_SYSTEM_PROMPT = `You are Hasna Assistant, a helpful AI assistant running in the terminal.
|
|
36144
36157
|
|
|
36145
36158
|
## Runtime Environment
|
|
36146
36159
|
- Use **Bun** as the default runtime for JavaScript/TypeScript scripts
|
|
@@ -36292,10 +36305,6 @@ function getConfigDir() {
|
|
|
36292
36305
|
if (assistantsOverride && assistantsOverride.trim()) {
|
|
36293
36306
|
return assistantsOverride;
|
|
36294
36307
|
}
|
|
36295
|
-
const legacyOverride = process.env.OLDPAL_DIR;
|
|
36296
|
-
if (legacyOverride && legacyOverride.trim()) {
|
|
36297
|
-
return legacyOverride;
|
|
36298
|
-
}
|
|
36299
36308
|
const envHome = process.env.HOME || process.env.USERPROFILE;
|
|
36300
36309
|
const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir();
|
|
36301
36310
|
return join(homeDir, ".assistants");
|
|
@@ -36313,12 +36322,10 @@ async function loadConfig(cwd2 = process.cwd()) {
|
|
|
36313
36322
|
const userConfig = await loadJsonFile(userConfigPath) || await loadJsonFile(legacyUserConfigPath);
|
|
36314
36323
|
config = mergeConfig(config, userConfig || undefined);
|
|
36315
36324
|
const projectConfigPath = join(getProjectConfigDir(cwd2), "config.json");
|
|
36316
|
-
const
|
|
36317
|
-
const projectConfig = await loadJsonFile(projectConfigPath) || await loadJsonFile(legacyProjectConfigPath);
|
|
36325
|
+
const projectConfig = await loadJsonFile(projectConfigPath);
|
|
36318
36326
|
config = mergeConfig(config, projectConfig || undefined);
|
|
36319
36327
|
const localConfigPath = join(getProjectConfigDir(cwd2), "config.local.json");
|
|
36320
|
-
const
|
|
36321
|
-
const localConfig = await loadJsonFile(localConfigPath) || await loadJsonFile(legacyLocalConfigPath);
|
|
36328
|
+
const localConfig = await loadJsonFile(localConfigPath);
|
|
36322
36329
|
config = mergeConfig(config, localConfig || undefined);
|
|
36323
36330
|
return config;
|
|
36324
36331
|
}
|
|
@@ -36377,13 +36384,11 @@ async function ensureConfigDir(sessionId) {
|
|
|
36377
36384
|
async function loadSystemPrompt(cwd2 = process.cwd()) {
|
|
36378
36385
|
const prompts = [];
|
|
36379
36386
|
const globalPromptPath = getConfigPath("ASSISTANTS.md");
|
|
36380
|
-
const
|
|
36381
|
-
const globalPrompt = await loadTextFile(globalPromptPath) ?? await loadTextFile(legacyGlobalPromptPath);
|
|
36387
|
+
const globalPrompt = await loadTextFile(globalPromptPath);
|
|
36382
36388
|
if (globalPrompt)
|
|
36383
36389
|
prompts.push(globalPrompt);
|
|
36384
36390
|
const projectPromptPath = join(getProjectConfigDir(cwd2), "ASSISTANTS.md");
|
|
36385
|
-
const
|
|
36386
|
-
const projectPrompt = await loadTextFile(projectPromptPath) ?? await loadTextFile(legacyProjectPromptPath);
|
|
36391
|
+
const projectPrompt = await loadTextFile(projectPromptPath);
|
|
36387
36392
|
if (projectPrompt)
|
|
36388
36393
|
prompts.push(projectPrompt);
|
|
36389
36394
|
if (prompts.length === 0) {
|
|
@@ -37379,7 +37384,6 @@ ${stderr || stdout}`.trim(), {
|
|
|
37379
37384
|
|
|
37380
37385
|
// packages/core/src/tools/filesystem.ts
|
|
37381
37386
|
import { join as join4, resolve as resolve3, dirname as dirname2, sep } from "path";
|
|
37382
|
-
import { existsSync as existsSync3 } from "fs";
|
|
37383
37387
|
init_errors();
|
|
37384
37388
|
var {Glob } = globalThis.Bun;
|
|
37385
37389
|
|
|
@@ -37469,10 +37473,6 @@ async function isPathSafe(targetPath, operation, options = {}) {
|
|
|
37469
37473
|
var currentSessionId = "default";
|
|
37470
37474
|
function getScriptsFolder(cwd2, sessionId) {
|
|
37471
37475
|
const resolvedSessionId = sessionId || currentSessionId;
|
|
37472
|
-
const legacyDir = join4(cwd2, ".oldpal");
|
|
37473
|
-
if (existsSync3(legacyDir)) {
|
|
37474
|
-
return join4(legacyDir, "scripts", resolvedSessionId);
|
|
37475
|
-
}
|
|
37476
37476
|
return join4(getProjectConfigDir(cwd2), "scripts", resolvedSessionId);
|
|
37477
37477
|
}
|
|
37478
37478
|
function isInScriptsFolder(path2, cwd2, sessionId) {
|
|
@@ -37608,7 +37608,7 @@ class FilesystemTools {
|
|
|
37608
37608
|
};
|
|
37609
37609
|
static writeTool = {
|
|
37610
37610
|
name: "write",
|
|
37611
|
-
description: "Write content to a file. RESTRICTED: Can only write to the project scripts folder (.
|
|
37611
|
+
description: "Write content to a file. RESTRICTED: Can only write to the project scripts folder (.assistants/scripts/{session}). Provide a filename and it will be saved under the scripts folder.",
|
|
37612
37612
|
parameters: {
|
|
37613
37613
|
type: "object",
|
|
37614
37614
|
properties: {
|
|
@@ -37894,16 +37894,22 @@ class WebFetchTool {
|
|
|
37894
37894
|
});
|
|
37895
37895
|
}
|
|
37896
37896
|
const controller = new AbortController;
|
|
37897
|
-
|
|
37898
|
-
|
|
37899
|
-
|
|
37900
|
-
|
|
37901
|
-
|
|
37902
|
-
|
|
37903
|
-
|
|
37897
|
+
let timeoutId = null;
|
|
37898
|
+
try {
|
|
37899
|
+
timeoutId = setTimeout(abortController, timeout, controller);
|
|
37900
|
+
response = await fetch(currentUrl, {
|
|
37901
|
+
signal: controller.signal,
|
|
37902
|
+
redirect: "manual",
|
|
37903
|
+
headers: {
|
|
37904
|
+
"User-Agent": "assistants/1.0 (AI Assistant)",
|
|
37905
|
+
Accept: extractType === "json" ? "application/json" : "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
|
|
37906
|
+
}
|
|
37907
|
+
});
|
|
37908
|
+
} finally {
|
|
37909
|
+
if (timeoutId) {
|
|
37910
|
+
clearTimeout(timeoutId);
|
|
37904
37911
|
}
|
|
37905
|
-
}
|
|
37906
|
-
clearTimeout(timeoutId);
|
|
37912
|
+
}
|
|
37907
37913
|
if ([301, 302, 303, 307, 308].includes(response.status)) {
|
|
37908
37914
|
const location = response.headers.get("location");
|
|
37909
37915
|
if (!location) {
|
|
@@ -38179,18 +38185,24 @@ class CurlTool {
|
|
|
38179
38185
|
});
|
|
38180
38186
|
}
|
|
38181
38187
|
const controller = new AbortController;
|
|
38182
|
-
|
|
38183
|
-
|
|
38184
|
-
|
|
38185
|
-
|
|
38186
|
-
|
|
38187
|
-
|
|
38188
|
-
|
|
38189
|
-
|
|
38190
|
-
|
|
38191
|
-
|
|
38192
|
-
|
|
38193
|
-
|
|
38188
|
+
let timeoutId = null;
|
|
38189
|
+
try {
|
|
38190
|
+
timeoutId = setTimeout(abortController, timeout, controller);
|
|
38191
|
+
response = await fetch(currentUrl, {
|
|
38192
|
+
method,
|
|
38193
|
+
signal: controller.signal,
|
|
38194
|
+
redirect: "manual",
|
|
38195
|
+
headers: {
|
|
38196
|
+
"User-Agent": "assistants/1.0 (AI Assistant)",
|
|
38197
|
+
...headers
|
|
38198
|
+
},
|
|
38199
|
+
body: body && ["POST", "PUT"].includes(method) ? body : undefined
|
|
38200
|
+
});
|
|
38201
|
+
} finally {
|
|
38202
|
+
if (timeoutId) {
|
|
38203
|
+
clearTimeout(timeoutId);
|
|
38204
|
+
}
|
|
38205
|
+
}
|
|
38194
38206
|
if ([301, 302, 303, 307, 308].includes(response.status)) {
|
|
38195
38207
|
if (!["GET", "HEAD"].includes(method)) {
|
|
38196
38208
|
throw new ToolExecutionError("Redirects are only supported for GET/HEAD requests", {
|
|
@@ -38395,7 +38407,7 @@ function isPrivateIPv4(octets) {
|
|
|
38395
38407
|
// packages/core/src/tools/feedback.ts
|
|
38396
38408
|
init_src();
|
|
38397
38409
|
import { join as join5 } from "path";
|
|
38398
|
-
import { existsSync as
|
|
38410
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
38399
38411
|
function normalizeTags(value) {
|
|
38400
38412
|
if (Array.isArray(value)) {
|
|
38401
38413
|
const tags = value.map((t) => String(t).trim()).filter(Boolean);
|
|
@@ -38409,9 +38421,9 @@ function normalizeTags(value) {
|
|
|
38409
38421
|
}
|
|
38410
38422
|
function resolveFeedbackDir(cwd2) {
|
|
38411
38423
|
const baseCwd = cwd2 && cwd2.trim().length > 0 ? cwd2 : process.cwd();
|
|
38412
|
-
const
|
|
38413
|
-
if (
|
|
38414
|
-
return join5(
|
|
38424
|
+
const projectConfigDir = join5(baseCwd, ".assistants");
|
|
38425
|
+
if (existsSync3(projectConfigDir)) {
|
|
38426
|
+
return join5(projectConfigDir, "feedback");
|
|
38415
38427
|
}
|
|
38416
38428
|
return join5(getConfigDir(), "feedback");
|
|
38417
38429
|
}
|
|
@@ -38734,7 +38746,7 @@ async function updateSchedule(cwd2, id, updater) {
|
|
|
38734
38746
|
return null;
|
|
38735
38747
|
}
|
|
38736
38748
|
}
|
|
38737
|
-
async function acquireScheduleLock(cwd2, id, ownerId, ttlMs = DEFAULT_LOCK_TTL_MS) {
|
|
38749
|
+
async function acquireScheduleLock(cwd2, id, ownerId, ttlMs = DEFAULT_LOCK_TTL_MS, allowRetry = true) {
|
|
38738
38750
|
await ensureDirs(cwd2);
|
|
38739
38751
|
const path2 = lockPath(cwd2, id);
|
|
38740
38752
|
const now2 = Date.now();
|
|
@@ -38751,9 +38763,18 @@ async function acquireScheduleLock(cwd2, id, ownerId, ttlMs = DEFAULT_LOCK_TTL_M
|
|
|
38751
38763
|
const ttl = lock?.ttlMs ?? ttlMs;
|
|
38752
38764
|
if (now2 - updatedAt > ttl) {
|
|
38753
38765
|
await unlink(path2);
|
|
38754
|
-
return acquireScheduleLock(cwd2, id, ownerId, ttlMs);
|
|
38766
|
+
return acquireScheduleLock(cwd2, id, ownerId, ttlMs, false);
|
|
38755
38767
|
}
|
|
38756
|
-
} catch {
|
|
38768
|
+
} catch {
|
|
38769
|
+
if (allowRetry) {
|
|
38770
|
+
try {
|
|
38771
|
+
await unlink(path2);
|
|
38772
|
+
return acquireScheduleLock(cwd2, id, ownerId, ttlMs, false);
|
|
38773
|
+
} catch {
|
|
38774
|
+
return false;
|
|
38775
|
+
}
|
|
38776
|
+
}
|
|
38777
|
+
}
|
|
38757
38778
|
}
|
|
38758
38779
|
return false;
|
|
38759
38780
|
}
|
|
@@ -38980,7 +39001,7 @@ class SchedulerTool {
|
|
|
38980
39001
|
|
|
38981
39002
|
// packages/core/src/tools/image.ts
|
|
38982
39003
|
init_src();
|
|
38983
|
-
import { existsSync as
|
|
39004
|
+
import { existsSync as existsSync4, writeFileSync as writeFileSync3, unlinkSync } from "fs";
|
|
38984
39005
|
import { tmpdir } from "os";
|
|
38985
39006
|
import { join as join7 } from "path";
|
|
38986
39007
|
import { homedir as homedir4 } from "os";
|
|
@@ -39067,7 +39088,7 @@ class ImageDisplayTool {
|
|
|
39067
39088
|
return `Error: Failed to fetch image: ${error instanceof Error ? error.message : String(error)}`;
|
|
39068
39089
|
}
|
|
39069
39090
|
}
|
|
39070
|
-
if (!
|
|
39091
|
+
if (!existsSync4(localPath)) {
|
|
39071
39092
|
return `Error: Image file not found: ${localPath}`;
|
|
39072
39093
|
}
|
|
39073
39094
|
try {
|
|
@@ -39092,7 +39113,7 @@ class ImageDisplayTool {
|
|
|
39092
39113
|
} catch (error) {
|
|
39093
39114
|
return `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
39094
39115
|
} finally {
|
|
39095
|
-
if (tempFile &&
|
|
39116
|
+
if (tempFile && existsSync4(tempFile)) {
|
|
39096
39117
|
try {
|
|
39097
39118
|
unlinkSync(tempFile);
|
|
39098
39119
|
} catch {}
|
|
@@ -39156,10 +39177,6 @@ class SkillLoader {
|
|
|
39156
39177
|
await this.loadFromDirectory(userSkillsDir);
|
|
39157
39178
|
const projectSkillsDir = join8(projectDir, ".assistants", "skills");
|
|
39158
39179
|
await this.loadFromDirectory(projectSkillsDir);
|
|
39159
|
-
const legacyUserSkillsDir = join8(userHome, ".oldpal", "skills");
|
|
39160
|
-
const legacyProjectSkillsDir = join8(projectDir, ".oldpal", "skills");
|
|
39161
|
-
await this.loadFromDirectory(legacyUserSkillsDir);
|
|
39162
|
-
await this.loadFromDirectory(legacyProjectSkillsDir);
|
|
39163
39180
|
const nestedGlob = new Glob2("**/.assistants/skills/*/SKILL.md");
|
|
39164
39181
|
for await (const file of nestedGlob.scan({ cwd: projectDir, dot: true })) {
|
|
39165
39182
|
await this.loadSkillFile(join8(projectDir, file));
|
|
@@ -39369,7 +39386,6 @@ class HookLoader {
|
|
|
39369
39386
|
this.hooks = {};
|
|
39370
39387
|
}
|
|
39371
39388
|
}
|
|
39372
|
-
|
|
39373
39389
|
// packages/core/src/hooks/executor.ts
|
|
39374
39390
|
init_src();
|
|
39375
39391
|
function killSpawnedProcess(proc) {
|
|
@@ -39595,211 +39611,720 @@ Respond with JSON only: {"allow": boolean, "reason": string}`;
|
|
|
39595
39611
|
return result;
|
|
39596
39612
|
}
|
|
39597
39613
|
}
|
|
39598
|
-
// packages/core/src/
|
|
39599
|
-
|
|
39600
|
-
|
|
39601
|
-
|
|
39602
|
-
|
|
39603
|
-
|
|
39604
|
-
|
|
39605
|
-
|
|
39606
|
-
|
|
39607
|
-
this.cwd = cwd2 || process.cwd();
|
|
39614
|
+
// packages/core/src/hooks/native.ts
|
|
39615
|
+
class NativeHookRegistry {
|
|
39616
|
+
hooks = new Map;
|
|
39617
|
+
config = {};
|
|
39618
|
+
register(hook) {
|
|
39619
|
+
const eventHooks = this.hooks.get(hook.event) || [];
|
|
39620
|
+
eventHooks.push(hook);
|
|
39621
|
+
eventHooks.sort((a, b) => a.priority - b.priority);
|
|
39622
|
+
this.hooks.set(hook.event, eventHooks);
|
|
39608
39623
|
}
|
|
39609
|
-
|
|
39610
|
-
this.
|
|
39611
|
-
const envHome = process.env.HOME || process.env.USERPROFILE;
|
|
39612
|
-
const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir6();
|
|
39613
|
-
const globalDir = join9(homeDir, ".assistants", "commands");
|
|
39614
|
-
await this.loadFromDirectory(globalDir, "global");
|
|
39615
|
-
const projectDir = join9(this.cwd, ".assistants", "commands");
|
|
39616
|
-
await this.loadFromDirectory(projectDir, "project");
|
|
39617
|
-
const legacyGlobalDir = join9(homeDir, ".oldpal", "commands");
|
|
39618
|
-
const legacyProjectDir = join9(this.cwd, ".oldpal", "commands");
|
|
39619
|
-
await this.loadFromDirectory(legacyGlobalDir, "global");
|
|
39620
|
-
await this.loadFromDirectory(legacyProjectDir, "project");
|
|
39624
|
+
getHooks(event) {
|
|
39625
|
+
return this.hooks.get(event) || [];
|
|
39621
39626
|
}
|
|
39622
|
-
|
|
39623
|
-
|
|
39624
|
-
|
|
39625
|
-
const entries = readdirSync2(dir);
|
|
39626
|
-
for (const entry of entries) {
|
|
39627
|
-
const fullPath = join9(dir, entry);
|
|
39628
|
-
const stat = statSync2(fullPath);
|
|
39629
|
-
if (stat.isDirectory()) {
|
|
39630
|
-
const newPrefix = prefix ? `${prefix}:${entry}` : entry;
|
|
39631
|
-
await this.loadFromDirectory(fullPath, source, newPrefix);
|
|
39632
|
-
} else if (stat.isFile() && extname(entry) === ".md") {
|
|
39633
|
-
const command = await this.loadCommandFile(fullPath, prefix);
|
|
39634
|
-
if (command) {
|
|
39635
|
-
this.commands.set(command.name, command);
|
|
39636
|
-
}
|
|
39637
|
-
}
|
|
39638
|
-
}
|
|
39627
|
+
hasHooks(event) {
|
|
39628
|
+
const hooks = this.hooks.get(event);
|
|
39629
|
+
return hooks !== undefined && hooks.length > 0;
|
|
39639
39630
|
}
|
|
39640
|
-
|
|
39641
|
-
|
|
39642
|
-
const content = await Bun.file(filePath).text();
|
|
39643
|
-
const { frontmatter, body } = this.parseFrontmatter(content);
|
|
39644
|
-
const fileName = basename2(filePath, ".md");
|
|
39645
|
-
const name = frontmatter.name || (prefix ? `${prefix}:${fileName}` : fileName);
|
|
39646
|
-
const allowedToolsRaw = frontmatter["allowed-tools"];
|
|
39647
|
-
const allowedTools = Array.isArray(allowedToolsRaw) ? allowedToolsRaw.map((t) => String(t).trim()).filter(Boolean) : typeof allowedToolsRaw === "string" ? allowedToolsRaw.split(",").map((t) => t.trim()).filter(Boolean) : undefined;
|
|
39648
|
-
return {
|
|
39649
|
-
name,
|
|
39650
|
-
description: frontmatter.description || `Run the ${name} command`,
|
|
39651
|
-
tags: frontmatter.tags,
|
|
39652
|
-
allowedTools,
|
|
39653
|
-
content: body,
|
|
39654
|
-
filePath,
|
|
39655
|
-
builtin: false
|
|
39656
|
-
};
|
|
39657
|
-
} catch (error) {
|
|
39658
|
-
console.error(`Failed to load command from ${filePath}:`, error);
|
|
39659
|
-
return null;
|
|
39660
|
-
}
|
|
39631
|
+
setConfig(config) {
|
|
39632
|
+
this.config = config;
|
|
39661
39633
|
}
|
|
39662
|
-
|
|
39663
|
-
|
|
39664
|
-
|
|
39665
|
-
|
|
39666
|
-
|
|
39634
|
+
getConfig() {
|
|
39635
|
+
return this.config;
|
|
39636
|
+
}
|
|
39637
|
+
async execute(event, input, context) {
|
|
39638
|
+
const hooks = this.getHooks(event);
|
|
39639
|
+
if (hooks.length === 0) {
|
|
39640
|
+
return null;
|
|
39667
39641
|
}
|
|
39668
|
-
const
|
|
39669
|
-
|
|
39670
|
-
|
|
39671
|
-
|
|
39672
|
-
|
|
39673
|
-
|
|
39674
|
-
const line = rawLine.trimEnd();
|
|
39675
|
-
if (!line.trim() || line.trim().startsWith("#"))
|
|
39676
|
-
continue;
|
|
39677
|
-
const listMatch = line.match(/^\s*-\s+(.+)$/);
|
|
39678
|
-
if (listMatch && currentListKey) {
|
|
39679
|
-
const list = frontmatter[currentListKey] ?? [];
|
|
39680
|
-
list.push(this.parseYamlValue(listMatch[1]));
|
|
39681
|
-
frontmatter[currentListKey] = list;
|
|
39642
|
+
const fullContext = {
|
|
39643
|
+
...context,
|
|
39644
|
+
config: this.config
|
|
39645
|
+
};
|
|
39646
|
+
for (const hook of hooks) {
|
|
39647
|
+
if (hook.enabled === false) {
|
|
39682
39648
|
continue;
|
|
39683
39649
|
}
|
|
39684
|
-
|
|
39685
|
-
if (!keyMatch)
|
|
39650
|
+
if (this.isHookDisabled(hook.id)) {
|
|
39686
39651
|
continue;
|
|
39687
|
-
|
|
39688
|
-
|
|
39689
|
-
|
|
39690
|
-
|
|
39691
|
-
|
|
39692
|
-
|
|
39652
|
+
}
|
|
39653
|
+
try {
|
|
39654
|
+
const result = await hook.handler(input, fullContext);
|
|
39655
|
+
if (result && result.continue === false) {
|
|
39656
|
+
return result;
|
|
39657
|
+
}
|
|
39658
|
+
if (result?.permissionDecision) {
|
|
39659
|
+
return result;
|
|
39693
39660
|
}
|
|
39661
|
+
} catch (error) {
|
|
39662
|
+
console.error(`Native hook ${hook.id} error:`, error);
|
|
39694
39663
|
continue;
|
|
39695
39664
|
}
|
|
39696
|
-
currentListKey = null;
|
|
39697
|
-
frontmatter[key] = this.parseYamlValue(valueRaw);
|
|
39698
39665
|
}
|
|
39699
|
-
return
|
|
39666
|
+
return null;
|
|
39700
39667
|
}
|
|
39701
|
-
|
|
39702
|
-
|
|
39703
|
-
|
|
39704
|
-
return true;
|
|
39705
|
-
if (trimmed === "false")
|
|
39706
|
-
return false;
|
|
39707
|
-
if (!Number.isNaN(Number(trimmed)) && trimmed !== "")
|
|
39708
|
-
return Number(trimmed);
|
|
39709
|
-
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
39710
|
-
const inner = trimmed.slice(1, -1).trim();
|
|
39711
|
-
if (!inner)
|
|
39712
|
-
return [];
|
|
39713
|
-
return inner.split(",").map((item) => this.parseYamlValue(item));
|
|
39714
|
-
}
|
|
39715
|
-
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
39716
|
-
return trimmed.slice(1, -1);
|
|
39668
|
+
isHookDisabled(hookId) {
|
|
39669
|
+
if (hookId === "scope-verification") {
|
|
39670
|
+
return this.config.scopeVerification?.enabled === false;
|
|
39717
39671
|
}
|
|
39718
|
-
return
|
|
39672
|
+
return false;
|
|
39719
39673
|
}
|
|
39720
|
-
|
|
39721
|
-
|
|
39674
|
+
listAll() {
|
|
39675
|
+
const result = [];
|
|
39676
|
+
for (const [event, hooks] of this.hooks.entries()) {
|
|
39677
|
+
result.push({ event, hooks });
|
|
39678
|
+
}
|
|
39679
|
+
return result;
|
|
39722
39680
|
}
|
|
39723
|
-
|
|
39724
|
-
|
|
39681
|
+
clear() {
|
|
39682
|
+
this.hooks.clear();
|
|
39725
39683
|
}
|
|
39726
|
-
|
|
39727
|
-
|
|
39684
|
+
}
|
|
39685
|
+
var nativeHookRegistry = new NativeHookRegistry;
|
|
39686
|
+
// packages/core/src/hooks/scope-context.ts
|
|
39687
|
+
init_src();
|
|
39688
|
+
var DEFAULT_EXCLUDE_PATTERNS = [
|
|
39689
|
+
/^(hi|hello|hey|good\s+(morning|afternoon|evening))[\s!.,]*$/i,
|
|
39690
|
+
/^(thanks|thank\s+you|ty|thx)[\s!.,]*$/i,
|
|
39691
|
+
/^(bye|goodbye|see\s+you|later)[\s!.,]*$/i,
|
|
39692
|
+
/^what\s+(is|are)\s+/i,
|
|
39693
|
+
/^how\s+(do|does|can|could)\s+/i,
|
|
39694
|
+
/^(can|could)\s+you\s+(tell|explain|describe)\s+/i,
|
|
39695
|
+
/^(who|when|where|why)\s+(is|are|was|were|did)\s+/i,
|
|
39696
|
+
/^\?+$/
|
|
39697
|
+
];
|
|
39698
|
+
|
|
39699
|
+
class ScopeContextManager {
|
|
39700
|
+
scopeContext = null;
|
|
39701
|
+
config;
|
|
39702
|
+
constructor(config = {}) {
|
|
39703
|
+
this.config = {
|
|
39704
|
+
enabled: config.enabled ?? true,
|
|
39705
|
+
maxRetries: config.maxRetries ?? 2,
|
|
39706
|
+
excludePatterns: config.excludePatterns ?? []
|
|
39707
|
+
};
|
|
39728
39708
|
}
|
|
39729
|
-
|
|
39730
|
-
|
|
39709
|
+
setConfig(config) {
|
|
39710
|
+
this.config = {
|
|
39711
|
+
enabled: config.enabled ?? this.config.enabled,
|
|
39712
|
+
maxRetries: config.maxRetries ?? this.config.maxRetries,
|
|
39713
|
+
excludePatterns: config.excludePatterns ?? this.config.excludePatterns
|
|
39714
|
+
};
|
|
39731
39715
|
}
|
|
39732
|
-
|
|
39733
|
-
|
|
39734
|
-
return this.getCommands().filter((cmd) => cmd.name.toLowerCase().startsWith(lower) || cmd.description.toLowerCase().includes(lower));
|
|
39716
|
+
isEnabled() {
|
|
39717
|
+
return this.config.enabled !== false;
|
|
39735
39718
|
}
|
|
39736
|
-
|
|
39737
|
-
|
|
39738
|
-
|
|
39739
|
-
|
|
39740
|
-
|
|
39741
|
-
|
|
39719
|
+
shouldExclude(message) {
|
|
39720
|
+
const trimmed = message.trim();
|
|
39721
|
+
for (const pattern of DEFAULT_EXCLUDE_PATTERNS) {
|
|
39722
|
+
if (pattern.test(trimmed)) {
|
|
39723
|
+
return true;
|
|
39724
|
+
}
|
|
39725
|
+
}
|
|
39726
|
+
const userPatterns = this.config.excludePatterns || [];
|
|
39727
|
+
for (const patternStr of userPatterns) {
|
|
39728
|
+
try {
|
|
39729
|
+
const pattern = new RegExp(patternStr, "i");
|
|
39730
|
+
if (pattern.test(trimmed)) {
|
|
39731
|
+
return true;
|
|
39732
|
+
}
|
|
39733
|
+
} catch {
|
|
39734
|
+
continue;
|
|
39735
|
+
}
|
|
39736
|
+
}
|
|
39737
|
+
if (trimmed.startsWith("!")) {
|
|
39738
|
+
return true;
|
|
39739
|
+
}
|
|
39740
|
+
return false;
|
|
39742
39741
|
}
|
|
39743
|
-
|
|
39744
|
-
|
|
39745
|
-
if (!trimmed.startsWith("/")) {
|
|
39742
|
+
async createContext(message, llmClient) {
|
|
39743
|
+
if (!this.isEnabled()) {
|
|
39746
39744
|
return null;
|
|
39747
39745
|
}
|
|
39748
|
-
|
|
39749
|
-
if (!match) {
|
|
39746
|
+
if (this.shouldExclude(message)) {
|
|
39750
39747
|
return null;
|
|
39751
39748
|
}
|
|
39752
|
-
|
|
39753
|
-
|
|
39754
|
-
|
|
39749
|
+
const goals = await this.extractGoals(message, llmClient);
|
|
39750
|
+
if (goals.length === 0) {
|
|
39751
|
+
return null;
|
|
39752
|
+
}
|
|
39753
|
+
this.scopeContext = {
|
|
39754
|
+
originalMessage: message,
|
|
39755
|
+
extractedGoals: goals,
|
|
39756
|
+
timestamp: Date.now(),
|
|
39757
|
+
verificationAttempts: 0,
|
|
39758
|
+
maxAttempts: this.config.maxRetries ?? 2
|
|
39755
39759
|
};
|
|
39760
|
+
return this.scopeContext;
|
|
39756
39761
|
}
|
|
39757
|
-
|
|
39758
|
-
|
|
39759
|
-
|
|
39760
|
-
async execute(input, context) {
|
|
39761
|
-
const parsed = this.parseCommand(input);
|
|
39762
|
-
if (!parsed) {
|
|
39763
|
-
return { handled: false };
|
|
39762
|
+
async extractGoals(message, llmClient) {
|
|
39763
|
+
if (!llmClient) {
|
|
39764
|
+
return this.extractGoalsHeuristic(message);
|
|
39764
39765
|
}
|
|
39765
|
-
|
|
39766
|
-
|
|
39767
|
-
|
|
39766
|
+
try {
|
|
39767
|
+
const prompt = `Analyze the following user request and extract the specific goals or tasks they want accomplished.
|
|
39768
|
+
Return a JSON array of strings, where each string is a concise, actionable goal.
|
|
39769
|
+
If the message is just a greeting, question, or doesn't contain actionable goals, return an empty array [].
|
|
39768
39770
|
|
|
39769
|
-
|
|
39770
|
-
|
|
39771
|
-
|
|
39772
|
-
|
|
39773
|
-
|
|
39774
|
-
|
|
39775
|
-
|
|
39771
|
+
User request: "${message}"
|
|
39772
|
+
|
|
39773
|
+
Respond with ONLY a JSON array, no other text. Example: ["goal 1", "goal 2"]`;
|
|
39774
|
+
const messages = [
|
|
39775
|
+
{ id: generateId(), role: "user", content: prompt, timestamp: Date.now() }
|
|
39776
|
+
];
|
|
39777
|
+
let response = "";
|
|
39778
|
+
for await (const chunk of llmClient.chat(messages)) {
|
|
39779
|
+
if (chunk.type === "text" && chunk.content) {
|
|
39780
|
+
response += chunk.content;
|
|
39781
|
+
}
|
|
39782
|
+
}
|
|
39783
|
+
const jsonMatch = response.match(/\[[\s\S]*\]/);
|
|
39784
|
+
if (jsonMatch) {
|
|
39785
|
+
const goals = JSON.parse(jsonMatch[0]);
|
|
39786
|
+
if (Array.isArray(goals) && goals.every((g) => typeof g === "string")) {
|
|
39787
|
+
return goals.filter((g) => g.trim().length > 0);
|
|
39788
|
+
}
|
|
39789
|
+
}
|
|
39790
|
+
return this.extractGoalsHeuristic(message);
|
|
39791
|
+
} catch (error) {
|
|
39792
|
+
console.error("Goal extraction error:", error);
|
|
39793
|
+
return this.extractGoalsHeuristic(message);
|
|
39776
39794
|
}
|
|
39777
|
-
const prompt = await this.preparePrompt(command, parsed.args, context);
|
|
39778
|
-
return {
|
|
39779
|
-
handled: false,
|
|
39780
|
-
prompt
|
|
39781
|
-
};
|
|
39782
|
-
}
|
|
39783
|
-
async preparePrompt(command, args, context) {
|
|
39784
|
-
let content = command.content;
|
|
39785
|
-
content = content.replace(/\$ARGUMENTS/g, args || "(no arguments provided)");
|
|
39786
|
-
content = await this.processShellCommands(content, context.cwd);
|
|
39787
|
-
return content;
|
|
39788
39795
|
}
|
|
39789
|
-
|
|
39790
|
-
const
|
|
39791
|
-
|
|
39792
|
-
|
|
39793
|
-
|
|
39794
|
-
|
|
39795
|
-
|
|
39796
|
-
|
|
39797
|
-
|
|
39798
|
-
|
|
39799
|
-
|
|
39800
|
-
|
|
39801
|
-
|
|
39802
|
-
|
|
39796
|
+
extractGoalsHeuristic(message) {
|
|
39797
|
+
const goals = [];
|
|
39798
|
+
const trimmed = message.trim();
|
|
39799
|
+
if (trimmed.length < 10) {
|
|
39800
|
+
return goals;
|
|
39801
|
+
}
|
|
39802
|
+
const taskIndicators = [
|
|
39803
|
+
/^(create|make|build|write|implement|add|fix|update|change|modify|remove|delete|refactor)/i,
|
|
39804
|
+
/^(find|search|look for|locate|get|fetch)/i,
|
|
39805
|
+
/^(run|execute|test|deploy|install|configure|setup)/i,
|
|
39806
|
+
/^(explain|describe|document|analyze|review)/i
|
|
39807
|
+
];
|
|
39808
|
+
for (const pattern of taskIndicators) {
|
|
39809
|
+
if (pattern.test(trimmed)) {
|
|
39810
|
+
goals.push(trimmed);
|
|
39811
|
+
return goals;
|
|
39812
|
+
}
|
|
39813
|
+
}
|
|
39814
|
+
const lines = trimmed.split(/\n/);
|
|
39815
|
+
for (const line of lines) {
|
|
39816
|
+
const listMatch = line.match(/^[\d\-\*\\u2022]\s*[.)\]:]?\s*(.+)/);
|
|
39817
|
+
if (listMatch && listMatch[1].trim().length > 5) {
|
|
39818
|
+
goals.push(listMatch[1].trim());
|
|
39819
|
+
}
|
|
39820
|
+
}
|
|
39821
|
+
if (goals.length === 0 && trimmed.length >= 20) {
|
|
39822
|
+
goals.push(trimmed);
|
|
39823
|
+
}
|
|
39824
|
+
return goals;
|
|
39825
|
+
}
|
|
39826
|
+
getContext() {
|
|
39827
|
+
return this.scopeContext;
|
|
39828
|
+
}
|
|
39829
|
+
setContext(context) {
|
|
39830
|
+
this.scopeContext = context;
|
|
39831
|
+
}
|
|
39832
|
+
incrementAttempts() {
|
|
39833
|
+
if (this.scopeContext) {
|
|
39834
|
+
this.scopeContext.verificationAttempts++;
|
|
39835
|
+
}
|
|
39836
|
+
}
|
|
39837
|
+
hasReachedMaxAttempts() {
|
|
39838
|
+
if (!this.scopeContext) {
|
|
39839
|
+
return true;
|
|
39840
|
+
}
|
|
39841
|
+
return this.scopeContext.verificationAttempts >= this.scopeContext.maxAttempts;
|
|
39842
|
+
}
|
|
39843
|
+
clear() {
|
|
39844
|
+
this.scopeContext = null;
|
|
39845
|
+
}
|
|
39846
|
+
}
|
|
39847
|
+
// packages/core/src/hooks/scope-verification.ts
|
|
39848
|
+
init_src();
|
|
39849
|
+
|
|
39850
|
+
// packages/core/src/sessions/verification.ts
|
|
39851
|
+
init_src();
|
|
39852
|
+
import { join as join9 } from "path";
|
|
39853
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4, readdirSync as readdirSync2 } from "fs";
|
|
39854
|
+
|
|
39855
|
+
class VerificationSessionStore {
|
|
39856
|
+
basePath;
|
|
39857
|
+
maxSessions;
|
|
39858
|
+
constructor(basePath, maxSessions = 100) {
|
|
39859
|
+
this.basePath = join9(basePath, "verifications");
|
|
39860
|
+
this.maxSessions = maxSessions;
|
|
39861
|
+
this.ensureDirectory();
|
|
39862
|
+
}
|
|
39863
|
+
ensureDirectory() {
|
|
39864
|
+
if (!existsSync5(this.basePath)) {
|
|
39865
|
+
mkdirSync3(this.basePath, { recursive: true });
|
|
39866
|
+
}
|
|
39867
|
+
}
|
|
39868
|
+
create(parentSessionId, goals, verificationResult) {
|
|
39869
|
+
const session = {
|
|
39870
|
+
id: generateId(),
|
|
39871
|
+
parentSessionId,
|
|
39872
|
+
type: "scope-verification",
|
|
39873
|
+
result: verificationResult.goalsMet ? "pass" : "fail",
|
|
39874
|
+
goals,
|
|
39875
|
+
reason: verificationResult.reason,
|
|
39876
|
+
suggestions: verificationResult.suggestions,
|
|
39877
|
+
verificationResult,
|
|
39878
|
+
createdAt: new Date().toISOString()
|
|
39879
|
+
};
|
|
39880
|
+
this.save(session);
|
|
39881
|
+
this.pruneOldSessions();
|
|
39882
|
+
return session;
|
|
39883
|
+
}
|
|
39884
|
+
save(session) {
|
|
39885
|
+
const filePath = join9(this.basePath, `${session.id}.json`);
|
|
39886
|
+
writeFileSync4(filePath, JSON.stringify(session, null, 2));
|
|
39887
|
+
}
|
|
39888
|
+
get(id) {
|
|
39889
|
+
const filePath = join9(this.basePath, `${id}.json`);
|
|
39890
|
+
if (!existsSync5(filePath)) {
|
|
39891
|
+
return null;
|
|
39892
|
+
}
|
|
39893
|
+
try {
|
|
39894
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
39895
|
+
return JSON.parse(content);
|
|
39896
|
+
} catch {
|
|
39897
|
+
return null;
|
|
39898
|
+
}
|
|
39899
|
+
}
|
|
39900
|
+
getByParentSession(parentSessionId) {
|
|
39901
|
+
const sessions = [];
|
|
39902
|
+
const files = this.listFiles();
|
|
39903
|
+
for (const file of files) {
|
|
39904
|
+
try {
|
|
39905
|
+
const content = readFileSync3(join9(this.basePath, file), "utf-8");
|
|
39906
|
+
const session = JSON.parse(content);
|
|
39907
|
+
if (session.parentSessionId === parentSessionId) {
|
|
39908
|
+
sessions.push(session);
|
|
39909
|
+
}
|
|
39910
|
+
} catch {
|
|
39911
|
+
continue;
|
|
39912
|
+
}
|
|
39913
|
+
}
|
|
39914
|
+
return sessions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
39915
|
+
}
|
|
39916
|
+
listRecent(limit = 10) {
|
|
39917
|
+
const sessions = [];
|
|
39918
|
+
const files = this.listFiles();
|
|
39919
|
+
for (const file of files) {
|
|
39920
|
+
try {
|
|
39921
|
+
const content = readFileSync3(join9(this.basePath, file), "utf-8");
|
|
39922
|
+
const session = JSON.parse(content);
|
|
39923
|
+
sessions.push(session);
|
|
39924
|
+
} catch {
|
|
39925
|
+
continue;
|
|
39926
|
+
}
|
|
39927
|
+
}
|
|
39928
|
+
return sessions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, limit);
|
|
39929
|
+
}
|
|
39930
|
+
updateResult(id, result) {
|
|
39931
|
+
const session = this.get(id);
|
|
39932
|
+
if (!session)
|
|
39933
|
+
return;
|
|
39934
|
+
session.result = result;
|
|
39935
|
+
this.save(session);
|
|
39936
|
+
}
|
|
39937
|
+
listFiles() {
|
|
39938
|
+
if (!existsSync5(this.basePath)) {
|
|
39939
|
+
return [];
|
|
39940
|
+
}
|
|
39941
|
+
return readdirSync2(this.basePath).filter((f) => f.endsWith(".json"));
|
|
39942
|
+
}
|
|
39943
|
+
pruneOldSessions() {
|
|
39944
|
+
const files = this.listFiles();
|
|
39945
|
+
if (files.length <= this.maxSessions) {
|
|
39946
|
+
return;
|
|
39947
|
+
}
|
|
39948
|
+
const sessions = [];
|
|
39949
|
+
for (const file of files) {
|
|
39950
|
+
try {
|
|
39951
|
+
const content = readFileSync3(join9(this.basePath, file), "utf-8");
|
|
39952
|
+
const session = JSON.parse(content);
|
|
39953
|
+
sessions.push({
|
|
39954
|
+
file,
|
|
39955
|
+
timestamp: new Date(session.createdAt).getTime()
|
|
39956
|
+
});
|
|
39957
|
+
} catch {
|
|
39958
|
+
continue;
|
|
39959
|
+
}
|
|
39960
|
+
}
|
|
39961
|
+
sessions.sort((a, b) => a.timestamp - b.timestamp);
|
|
39962
|
+
const toRemove = sessions.slice(0, sessions.length - this.maxSessions);
|
|
39963
|
+
for (const item of toRemove) {
|
|
39964
|
+
try {
|
|
39965
|
+
const { unlinkSync: unlinkSync2 } = __require("fs");
|
|
39966
|
+
unlinkSync2(join9(this.basePath, item.file));
|
|
39967
|
+
} catch {
|
|
39968
|
+
continue;
|
|
39969
|
+
}
|
|
39970
|
+
}
|
|
39971
|
+
}
|
|
39972
|
+
clear() {
|
|
39973
|
+
const files = this.listFiles();
|
|
39974
|
+
for (const file of files) {
|
|
39975
|
+
try {
|
|
39976
|
+
const { unlinkSync: unlinkSync2 } = __require("fs");
|
|
39977
|
+
unlinkSync2(join9(this.basePath, file));
|
|
39978
|
+
} catch {
|
|
39979
|
+
continue;
|
|
39980
|
+
}
|
|
39981
|
+
}
|
|
39982
|
+
}
|
|
39983
|
+
}
|
|
39984
|
+
|
|
39985
|
+
// packages/core/src/hooks/scope-verification.ts
|
|
39986
|
+
var VERIFICATION_PROMPT = `You are a goal verification assistant. Your task is to determine if the user's original goals were accomplished based on the conversation history.
|
|
39987
|
+
|
|
39988
|
+
Original request: {originalMessage}
|
|
39989
|
+
|
|
39990
|
+
Extracted goals:
|
|
39991
|
+
{goals}
|
|
39992
|
+
|
|
39993
|
+
Analyze the conversation history that follows. For each goal, determine if it was addressed and provide evidence.
|
|
39994
|
+
|
|
39995
|
+
Respond with ONLY valid JSON in this exact format:
|
|
39996
|
+
{
|
|
39997
|
+
"goalsMet": boolean,
|
|
39998
|
+
"goalsAnalysis": [
|
|
39999
|
+
{"goal": "string", "met": boolean, "evidence": "string"}
|
|
40000
|
+
],
|
|
40001
|
+
"reason": "string explaining overall assessment",
|
|
40002
|
+
"suggestions": ["string"] // Only include if goalsMet is false, listing what still needs to be done
|
|
40003
|
+
}`;
|
|
40004
|
+
function summarizeConversation(messages) {
|
|
40005
|
+
const relevant = messages.filter((m) => m.role !== "system");
|
|
40006
|
+
const summary = [];
|
|
40007
|
+
for (const msg of relevant.slice(-20)) {
|
|
40008
|
+
if (msg.role === "user") {
|
|
40009
|
+
if (msg.toolResults && msg.toolResults.length > 0) {
|
|
40010
|
+
for (const result of msg.toolResults) {
|
|
40011
|
+
const content = result.content.slice(0, 500);
|
|
40012
|
+
summary.push(`[Tool Result - ${result.toolName || "unknown"}]: ${content}`);
|
|
40013
|
+
}
|
|
40014
|
+
} else {
|
|
40015
|
+
summary.push(`User: ${msg.content.slice(0, 300)}`);
|
|
40016
|
+
}
|
|
40017
|
+
} else if (msg.role === "assistant") {
|
|
40018
|
+
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
40019
|
+
for (const call of msg.toolCalls) {
|
|
40020
|
+
const input = JSON.stringify(call.input).slice(0, 200);
|
|
40021
|
+
summary.push(`[Tool Call - ${call.name}]: ${input}`);
|
|
40022
|
+
}
|
|
40023
|
+
}
|
|
40024
|
+
if (msg.content.trim()) {
|
|
40025
|
+
summary.push(`Assistant: ${msg.content.slice(0, 300)}`);
|
|
40026
|
+
}
|
|
40027
|
+
}
|
|
40028
|
+
}
|
|
40029
|
+
return summary.join(`
|
|
40030
|
+
|
|
40031
|
+
`);
|
|
40032
|
+
}
|
|
40033
|
+
async function runVerification(scopeContext, messages, llmClient) {
|
|
40034
|
+
const goalsText = scopeContext.extractedGoals.map((g, i) => `${i + 1}. ${g}`).join(`
|
|
40035
|
+
`);
|
|
40036
|
+
const prompt = VERIFICATION_PROMPT.replace("{originalMessage}", scopeContext.originalMessage).replace("{goals}", goalsText);
|
|
40037
|
+
const conversationSummary = summarizeConversation(messages);
|
|
40038
|
+
const verificationMessages = [
|
|
40039
|
+
{
|
|
40040
|
+
id: generateId(),
|
|
40041
|
+
role: "user",
|
|
40042
|
+
content: `${prompt}
|
|
40043
|
+
|
|
40044
|
+
---
|
|
40045
|
+
|
|
40046
|
+
Conversation history:
|
|
40047
|
+
${conversationSummary}`,
|
|
40048
|
+
timestamp: Date.now()
|
|
40049
|
+
}
|
|
40050
|
+
];
|
|
40051
|
+
let response = "";
|
|
40052
|
+
for await (const chunk of llmClient.chat(verificationMessages)) {
|
|
40053
|
+
if (chunk.type === "text" && chunk.content) {
|
|
40054
|
+
response += chunk.content;
|
|
40055
|
+
}
|
|
40056
|
+
}
|
|
40057
|
+
try {
|
|
40058
|
+
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
40059
|
+
if (jsonMatch) {
|
|
40060
|
+
const result = JSON.parse(jsonMatch[0]);
|
|
40061
|
+
if (typeof result.goalsMet === "boolean" && typeof result.reason === "string" && Array.isArray(result.goalsAnalysis)) {
|
|
40062
|
+
return result;
|
|
40063
|
+
}
|
|
40064
|
+
}
|
|
40065
|
+
} catch {}
|
|
40066
|
+
return {
|
|
40067
|
+
goalsMet: true,
|
|
40068
|
+
goalsAnalysis: scopeContext.extractedGoals.map((goal) => ({
|
|
40069
|
+
goal,
|
|
40070
|
+
met: true,
|
|
40071
|
+
evidence: "Unable to verify - defaulting to complete"
|
|
40072
|
+
})),
|
|
40073
|
+
reason: "Verification parsing failed - assuming goals were met"
|
|
40074
|
+
};
|
|
40075
|
+
}
|
|
40076
|
+
async function scopeVerificationHandler(input, context) {
|
|
40077
|
+
const { scopeContext, messages, config, sessionId, cwd: cwd2 } = context;
|
|
40078
|
+
const llmClient = context.llmClient;
|
|
40079
|
+
if (!scopeContext || !llmClient) {
|
|
40080
|
+
return null;
|
|
40081
|
+
}
|
|
40082
|
+
if (scopeContext.verificationAttempts >= scopeContext.maxAttempts) {
|
|
40083
|
+
return null;
|
|
40084
|
+
}
|
|
40085
|
+
if (config?.scopeVerification?.enabled === false) {
|
|
40086
|
+
return null;
|
|
40087
|
+
}
|
|
40088
|
+
if (scopeContext.extractedGoals.length === 0) {
|
|
40089
|
+
return null;
|
|
40090
|
+
}
|
|
40091
|
+
try {
|
|
40092
|
+
const result = await runVerification(scopeContext, messages, llmClient);
|
|
40093
|
+
const store = new VerificationSessionStore(getConfigDir());
|
|
40094
|
+
const session = store.create(sessionId, scopeContext.extractedGoals, result);
|
|
40095
|
+
if (result.goalsMet) {
|
|
40096
|
+
return null;
|
|
40097
|
+
}
|
|
40098
|
+
const suggestions = result.suggestions?.join(`
|
|
40099
|
+
- `) || "Please complete the remaining tasks.";
|
|
40100
|
+
return {
|
|
40101
|
+
continue: false,
|
|
40102
|
+
stopReason: "Goals not fully achieved",
|
|
40103
|
+
systemMessage: `[Scope Verification - Session ${session.id}]
|
|
40104
|
+
The following goals were not completed:
|
|
40105
|
+
${result.goalsAnalysis.filter((g) => !g.met).map((g) => `- ${g.goal}: ${g.evidence}`).join(`
|
|
40106
|
+
`)}
|
|
40107
|
+
|
|
40108
|
+
${result.reason}
|
|
40109
|
+
|
|
40110
|
+
Please continue and:
|
|
40111
|
+
- ${suggestions}`,
|
|
40112
|
+
additionalContext: `Verification session: ${session.id}`
|
|
40113
|
+
};
|
|
40114
|
+
} catch (error) {
|
|
40115
|
+
console.error("Scope verification error:", error);
|
|
40116
|
+
return null;
|
|
40117
|
+
}
|
|
40118
|
+
}
|
|
40119
|
+
function createScopeVerificationHook() {
|
|
40120
|
+
return {
|
|
40121
|
+
id: "scope-verification",
|
|
40122
|
+
event: "Stop",
|
|
40123
|
+
priority: 100,
|
|
40124
|
+
handler: scopeVerificationHandler
|
|
40125
|
+
};
|
|
40126
|
+
}
|
|
40127
|
+
// packages/core/src/commands/loader.ts
|
|
40128
|
+
import { existsSync as existsSync6, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
|
|
40129
|
+
import { join as join10, basename as basename2, extname } from "path";
|
|
40130
|
+
import { homedir as homedir6 } from "os";
|
|
40131
|
+
|
|
40132
|
+
class CommandLoader {
|
|
40133
|
+
commands = new Map;
|
|
40134
|
+
cwd;
|
|
40135
|
+
constructor(cwd2) {
|
|
40136
|
+
this.cwd = cwd2 || process.cwd();
|
|
40137
|
+
}
|
|
40138
|
+
async loadAll() {
|
|
40139
|
+
this.commands.clear();
|
|
40140
|
+
const envHome = process.env.HOME || process.env.USERPROFILE;
|
|
40141
|
+
const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir6();
|
|
40142
|
+
const globalDir = join10(homeDir, ".assistants", "commands");
|
|
40143
|
+
await this.loadFromDirectory(globalDir, "global");
|
|
40144
|
+
const projectDir = join10(this.cwd, ".assistants", "commands");
|
|
40145
|
+
await this.loadFromDirectory(projectDir, "project");
|
|
40146
|
+
}
|
|
40147
|
+
async loadFromDirectory(dir, source, prefix = "") {
|
|
40148
|
+
if (!existsSync6(dir))
|
|
40149
|
+
return;
|
|
40150
|
+
const entries = readdirSync3(dir);
|
|
40151
|
+
for (const entry of entries) {
|
|
40152
|
+
const fullPath = join10(dir, entry);
|
|
40153
|
+
const stat = statSync2(fullPath);
|
|
40154
|
+
if (stat.isDirectory()) {
|
|
40155
|
+
const newPrefix = prefix ? `${prefix}:${entry}` : entry;
|
|
40156
|
+
await this.loadFromDirectory(fullPath, source, newPrefix);
|
|
40157
|
+
} else if (stat.isFile() && extname(entry) === ".md") {
|
|
40158
|
+
const command = await this.loadCommandFile(fullPath, prefix);
|
|
40159
|
+
if (command) {
|
|
40160
|
+
this.commands.set(command.name, command);
|
|
40161
|
+
}
|
|
40162
|
+
}
|
|
40163
|
+
}
|
|
40164
|
+
}
|
|
40165
|
+
async loadCommandFile(filePath, prefix) {
|
|
40166
|
+
try {
|
|
40167
|
+
const content = await Bun.file(filePath).text();
|
|
40168
|
+
const { frontmatter, body } = this.parseFrontmatter(content);
|
|
40169
|
+
const fileName = basename2(filePath, ".md");
|
|
40170
|
+
const name = frontmatter.name || (prefix ? `${prefix}:${fileName}` : fileName);
|
|
40171
|
+
const allowedToolsRaw = frontmatter["allowed-tools"];
|
|
40172
|
+
const allowedTools = Array.isArray(allowedToolsRaw) ? allowedToolsRaw.map((t) => String(t).trim()).filter(Boolean) : typeof allowedToolsRaw === "string" ? allowedToolsRaw.split(",").map((t) => t.trim()).filter(Boolean) : undefined;
|
|
40173
|
+
return {
|
|
40174
|
+
name,
|
|
40175
|
+
description: frontmatter.description || `Run the ${name} command`,
|
|
40176
|
+
tags: frontmatter.tags,
|
|
40177
|
+
allowedTools,
|
|
40178
|
+
content: body,
|
|
40179
|
+
filePath,
|
|
40180
|
+
builtin: false
|
|
40181
|
+
};
|
|
40182
|
+
} catch (error) {
|
|
40183
|
+
console.error(`Failed to load command from ${filePath}:`, error);
|
|
40184
|
+
return null;
|
|
40185
|
+
}
|
|
40186
|
+
}
|
|
40187
|
+
parseFrontmatter(content) {
|
|
40188
|
+
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
|
|
40189
|
+
const match = content.match(frontmatterRegex);
|
|
40190
|
+
if (!match) {
|
|
40191
|
+
return { frontmatter: {}, body: content };
|
|
40192
|
+
}
|
|
40193
|
+
const [, yamlContent, body] = match;
|
|
40194
|
+
const frontmatter = {};
|
|
40195
|
+
const lines = yamlContent.split(`
|
|
40196
|
+
`);
|
|
40197
|
+
let currentListKey = null;
|
|
40198
|
+
for (const rawLine of lines) {
|
|
40199
|
+
const line = rawLine.trimEnd();
|
|
40200
|
+
if (!line.trim() || line.trim().startsWith("#"))
|
|
40201
|
+
continue;
|
|
40202
|
+
const listMatch = line.match(/^\s*-\s+(.+)$/);
|
|
40203
|
+
if (listMatch && currentListKey) {
|
|
40204
|
+
const list = frontmatter[currentListKey] ?? [];
|
|
40205
|
+
list.push(this.parseYamlValue(listMatch[1]));
|
|
40206
|
+
frontmatter[currentListKey] = list;
|
|
40207
|
+
continue;
|
|
40208
|
+
}
|
|
40209
|
+
const keyMatch = line.match(/^([A-Za-z0-9_-]+)\s*:\s*(.*)$/);
|
|
40210
|
+
if (!keyMatch)
|
|
40211
|
+
continue;
|
|
40212
|
+
const key = keyMatch[1];
|
|
40213
|
+
const valueRaw = keyMatch[2] ?? "";
|
|
40214
|
+
if (valueRaw.trim() === "") {
|
|
40215
|
+
currentListKey = key;
|
|
40216
|
+
if (!frontmatter[key]) {
|
|
40217
|
+
frontmatter[key] = [];
|
|
40218
|
+
}
|
|
40219
|
+
continue;
|
|
40220
|
+
}
|
|
40221
|
+
currentListKey = null;
|
|
40222
|
+
frontmatter[key] = this.parseYamlValue(valueRaw);
|
|
40223
|
+
}
|
|
40224
|
+
return { frontmatter, body: body.trim() };
|
|
40225
|
+
}
|
|
40226
|
+
parseYamlValue(value) {
|
|
40227
|
+
const trimmed = value.trim();
|
|
40228
|
+
if (trimmed === "true")
|
|
40229
|
+
return true;
|
|
40230
|
+
if (trimmed === "false")
|
|
40231
|
+
return false;
|
|
40232
|
+
if (!Number.isNaN(Number(trimmed)) && trimmed !== "")
|
|
40233
|
+
return Number(trimmed);
|
|
40234
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
40235
|
+
const inner = trimmed.slice(1, -1).trim();
|
|
40236
|
+
if (!inner)
|
|
40237
|
+
return [];
|
|
40238
|
+
return inner.split(",").map((item) => this.parseYamlValue(item));
|
|
40239
|
+
}
|
|
40240
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
40241
|
+
return trimmed.slice(1, -1);
|
|
40242
|
+
}
|
|
40243
|
+
return trimmed;
|
|
40244
|
+
}
|
|
40245
|
+
register(command) {
|
|
40246
|
+
this.commands.set(command.name, command);
|
|
40247
|
+
}
|
|
40248
|
+
getCommand(name) {
|
|
40249
|
+
return this.commands.get(name);
|
|
40250
|
+
}
|
|
40251
|
+
getCommands() {
|
|
40252
|
+
return Array.from(this.commands.values());
|
|
40253
|
+
}
|
|
40254
|
+
hasCommand(name) {
|
|
40255
|
+
return this.commands.has(name);
|
|
40256
|
+
}
|
|
40257
|
+
findMatching(partial) {
|
|
40258
|
+
const lower = partial.toLowerCase();
|
|
40259
|
+
return this.getCommands().filter((cmd) => cmd.name.toLowerCase().startsWith(lower) || cmd.description.toLowerCase().includes(lower));
|
|
40260
|
+
}
|
|
40261
|
+
}
|
|
40262
|
+
// packages/core/src/commands/executor.ts
|
|
40263
|
+
class CommandExecutor {
|
|
40264
|
+
loader;
|
|
40265
|
+
constructor(loader) {
|
|
40266
|
+
this.loader = loader;
|
|
40267
|
+
}
|
|
40268
|
+
parseCommand(input) {
|
|
40269
|
+
const trimmed = input.trim();
|
|
40270
|
+
if (!trimmed.startsWith("/")) {
|
|
40271
|
+
return null;
|
|
40272
|
+
}
|
|
40273
|
+
const match = trimmed.match(/^\/(\S+)(?:\s+(.*))?$/);
|
|
40274
|
+
if (!match) {
|
|
40275
|
+
return null;
|
|
40276
|
+
}
|
|
40277
|
+
return {
|
|
40278
|
+
name: match[1],
|
|
40279
|
+
args: match[2] || ""
|
|
40280
|
+
};
|
|
40281
|
+
}
|
|
40282
|
+
isCommand(input) {
|
|
40283
|
+
return this.parseCommand(input) !== null;
|
|
40284
|
+
}
|
|
40285
|
+
async execute(input, context) {
|
|
40286
|
+
const parsed = this.parseCommand(input);
|
|
40287
|
+
if (!parsed) {
|
|
40288
|
+
return { handled: false };
|
|
40289
|
+
}
|
|
40290
|
+
const command = this.loader.getCommand(parsed.name);
|
|
40291
|
+
if (!command) {
|
|
40292
|
+
context.emit("text", `Unknown command: /${parsed.name}
|
|
40293
|
+
|
|
40294
|
+
Use /help to see available commands.
|
|
40295
|
+
`);
|
|
40296
|
+
context.emit("done");
|
|
40297
|
+
return { handled: true };
|
|
40298
|
+
}
|
|
40299
|
+
if (command.selfHandled && command.handler) {
|
|
40300
|
+
return command.handler(parsed.args, context);
|
|
40301
|
+
}
|
|
40302
|
+
const prompt = await this.preparePrompt(command, parsed.args, context);
|
|
40303
|
+
return {
|
|
40304
|
+
handled: false,
|
|
40305
|
+
prompt
|
|
40306
|
+
};
|
|
40307
|
+
}
|
|
40308
|
+
async preparePrompt(command, args, context) {
|
|
40309
|
+
let content = command.content;
|
|
40310
|
+
content = content.replace(/\$ARGUMENTS/g, args || "(no arguments provided)");
|
|
40311
|
+
content = await this.processShellCommands(content, context.cwd);
|
|
40312
|
+
return content;
|
|
40313
|
+
}
|
|
40314
|
+
async processShellCommands(content, cwd2) {
|
|
40315
|
+
const lines = content.split(`
|
|
40316
|
+
`);
|
|
40317
|
+
const processedLines = [];
|
|
40318
|
+
for (const line of lines) {
|
|
40319
|
+
const trimmed = line.trim();
|
|
40320
|
+
if (trimmed.startsWith("!")) {
|
|
40321
|
+
const command = trimmed.slice(1).trim();
|
|
40322
|
+
const output = await this.executeShell(command, cwd2);
|
|
40323
|
+
processedLines.push(`\`\`\`
|
|
40324
|
+
${output}
|
|
40325
|
+
\`\`\``);
|
|
40326
|
+
} else {
|
|
40327
|
+
processedLines.push(line);
|
|
39803
40328
|
}
|
|
39804
40329
|
}
|
|
39805
40330
|
return processedLines.join(`
|
|
@@ -39849,20 +40374,20 @@ ${stderr}`;
|
|
|
39849
40374
|
}
|
|
39850
40375
|
}
|
|
39851
40376
|
// packages/core/src/commands/builtin.ts
|
|
39852
|
-
import { join as
|
|
40377
|
+
import { join as join12 } from "path";
|
|
39853
40378
|
import { homedir as homedir7, platform as platform2, release, arch } from "os";
|
|
39854
|
-
import { existsSync as existsSync7, mkdirSync as
|
|
40379
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
39855
40380
|
init_src();
|
|
39856
40381
|
|
|
39857
40382
|
// packages/core/src/projects/store.ts
|
|
39858
40383
|
init_src();
|
|
39859
|
-
import { join as
|
|
40384
|
+
import { join as join11 } from "path";
|
|
39860
40385
|
import { mkdir as mkdir3, readdir as readdir2, readFile as readFile2, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
|
|
39861
40386
|
function projectsDir(cwd2) {
|
|
39862
|
-
return
|
|
40387
|
+
return join11(cwd2, ".assistants", "projects");
|
|
39863
40388
|
}
|
|
39864
40389
|
function projectPath(cwd2, id) {
|
|
39865
|
-
return
|
|
40390
|
+
return join11(projectsDir(cwd2), `${id}.json`);
|
|
39866
40391
|
}
|
|
39867
40392
|
async function ensureProjectsDir(cwd2) {
|
|
39868
40393
|
await mkdir3(projectsDir(cwd2), { recursive: true });
|
|
@@ -39879,7 +40404,7 @@ async function listProjects(cwd2) {
|
|
|
39879
40404
|
if (!file.endsWith(".json"))
|
|
39880
40405
|
continue;
|
|
39881
40406
|
try {
|
|
39882
|
-
const raw = await readFile2(
|
|
40407
|
+
const raw = await readFile2(join11(dir, file), "utf-8");
|
|
39883
40408
|
const parsed = JSON.parse(raw);
|
|
39884
40409
|
if (parsed?.id && parsed?.name) {
|
|
39885
40410
|
projects.push(parsed);
|
|
@@ -40057,7 +40582,7 @@ async function buildProjectContext(project, options) {
|
|
|
40057
40582
|
}
|
|
40058
40583
|
|
|
40059
40584
|
// packages/core/src/commands/builtin.ts
|
|
40060
|
-
var VERSION = process.env.ASSISTANTS_VERSION || process.env.npm_package_version ||
|
|
40585
|
+
var VERSION = process.env.ASSISTANTS_VERSION || process.env.npm_package_version || "unknown";
|
|
40061
40586
|
function resolveAuthTimeout(resolve5) {
|
|
40062
40587
|
resolve5({ exitCode: 1, stdout: { toString: () => "{}" } });
|
|
40063
40588
|
}
|
|
@@ -40133,6 +40658,7 @@ class BuiltinCommands {
|
|
|
40133
40658
|
loader.register(this.resumeScheduleCommand());
|
|
40134
40659
|
loader.register(this.connectorsCommand());
|
|
40135
40660
|
loader.register(this.securityLogCommand());
|
|
40661
|
+
loader.register(this.verificationCommand());
|
|
40136
40662
|
loader.register(this.exitCommand());
|
|
40137
40663
|
}
|
|
40138
40664
|
voiceCommand() {
|
|
@@ -40642,6 +41168,130 @@ Identities:
|
|
|
40642
41168
|
}
|
|
40643
41169
|
};
|
|
40644
41170
|
}
|
|
41171
|
+
verificationCommand() {
|
|
41172
|
+
return {
|
|
41173
|
+
name: "verification",
|
|
41174
|
+
description: "Manage scope verification (list/view/enable/disable)",
|
|
41175
|
+
builtin: true,
|
|
41176
|
+
selfHandled: true,
|
|
41177
|
+
content: "",
|
|
41178
|
+
handler: async (args, context) => {
|
|
41179
|
+
const arg = args.trim().toLowerCase();
|
|
41180
|
+
const store = new VerificationSessionStore(getConfigDir());
|
|
41181
|
+
if (arg === "disable" || arg === "off") {
|
|
41182
|
+
nativeHookRegistry.setConfig({
|
|
41183
|
+
...nativeHookRegistry.getConfig(),
|
|
41184
|
+
scopeVerification: {
|
|
41185
|
+
...nativeHookRegistry.getConfig().scopeVerification,
|
|
41186
|
+
enabled: false
|
|
41187
|
+
}
|
|
41188
|
+
});
|
|
41189
|
+
context.emit("text", `Scope verification disabled.
|
|
41190
|
+
`);
|
|
41191
|
+
context.emit("done");
|
|
41192
|
+
return { handled: true };
|
|
41193
|
+
}
|
|
41194
|
+
if (arg === "enable" || arg === "on") {
|
|
41195
|
+
nativeHookRegistry.setConfig({
|
|
41196
|
+
...nativeHookRegistry.getConfig(),
|
|
41197
|
+
scopeVerification: {
|
|
41198
|
+
...nativeHookRegistry.getConfig().scopeVerification,
|
|
41199
|
+
enabled: true
|
|
41200
|
+
}
|
|
41201
|
+
});
|
|
41202
|
+
context.emit("text", `Scope verification enabled.
|
|
41203
|
+
`);
|
|
41204
|
+
context.emit("done");
|
|
41205
|
+
return { handled: true };
|
|
41206
|
+
}
|
|
41207
|
+
if (arg === "status") {
|
|
41208
|
+
const config = nativeHookRegistry.getConfig();
|
|
41209
|
+
const enabled = config.scopeVerification?.enabled !== false;
|
|
41210
|
+
const maxRetries = config.scopeVerification?.maxRetries ?? 2;
|
|
41211
|
+
context.emit("text", `Scope verification: ${enabled ? "enabled" : "disabled"}
|
|
41212
|
+
`);
|
|
41213
|
+
context.emit("text", `Max retries: ${maxRetries}
|
|
41214
|
+
`);
|
|
41215
|
+
context.emit("done");
|
|
41216
|
+
return { handled: true };
|
|
41217
|
+
}
|
|
41218
|
+
if (arg === "" || arg === "list") {
|
|
41219
|
+
const sessions2 = store.listRecent(10);
|
|
41220
|
+
if (sessions2.length === 0) {
|
|
41221
|
+
context.emit("text", `No verification sessions found.
|
|
41222
|
+
`);
|
|
41223
|
+
context.emit("done");
|
|
41224
|
+
return { handled: true };
|
|
41225
|
+
}
|
|
41226
|
+
context.emit("text", `Recent verification sessions:
|
|
41227
|
+
|
|
41228
|
+
`);
|
|
41229
|
+
for (const session of sessions2) {
|
|
41230
|
+
const date = new Date(session.createdAt).toLocaleString();
|
|
41231
|
+
const status = session.result === "pass" ? "\u2713" : session.result === "force-continue" ? "\u2192" : "\u2717";
|
|
41232
|
+
context.emit("text", `${status} ${session.id.slice(0, 8)} - ${date} - ${session.result}
|
|
41233
|
+
`);
|
|
41234
|
+
context.emit("text", ` Goals: ${session.goals.slice(0, 2).join(", ")}${session.goals.length > 2 ? "..." : ""}
|
|
41235
|
+
`);
|
|
41236
|
+
}
|
|
41237
|
+
context.emit("text", `
|
|
41238
|
+
Use /verification <id> to view details.
|
|
41239
|
+
`);
|
|
41240
|
+
context.emit("done");
|
|
41241
|
+
return { handled: true };
|
|
41242
|
+
}
|
|
41243
|
+
const sessions = store.listRecent(100);
|
|
41244
|
+
const match = sessions.find((s) => s.id.startsWith(arg) || s.id === arg);
|
|
41245
|
+
if (!match) {
|
|
41246
|
+
context.emit("text", `No verification session found matching "${arg}".
|
|
41247
|
+
`);
|
|
41248
|
+
context.emit("done");
|
|
41249
|
+
return { handled: true };
|
|
41250
|
+
}
|
|
41251
|
+
context.emit("text", `
|
|
41252
|
+
=== Verification Session ${match.id} ===
|
|
41253
|
+
|
|
41254
|
+
`);
|
|
41255
|
+
context.emit("text", `Created: ${new Date(match.createdAt).toLocaleString()}
|
|
41256
|
+
`);
|
|
41257
|
+
context.emit("text", `Parent Session: ${match.parentSessionId}
|
|
41258
|
+
`);
|
|
41259
|
+
context.emit("text", `Result: ${match.result}
|
|
41260
|
+
|
|
41261
|
+
`);
|
|
41262
|
+
context.emit("text", `Goals:
|
|
41263
|
+
`);
|
|
41264
|
+
for (const goal of match.goals) {
|
|
41265
|
+
context.emit("text", ` \u2022 ${goal}
|
|
41266
|
+
`);
|
|
41267
|
+
}
|
|
41268
|
+
context.emit("text", `
|
|
41269
|
+
Analysis:
|
|
41270
|
+
`);
|
|
41271
|
+
for (const analysis of match.verificationResult.goalsAnalysis) {
|
|
41272
|
+
const icon = analysis.met ? "\u2713" : "\u2717";
|
|
41273
|
+
context.emit("text", ` ${icon} ${analysis.goal}
|
|
41274
|
+
`);
|
|
41275
|
+
context.emit("text", ` ${analysis.evidence}
|
|
41276
|
+
`);
|
|
41277
|
+
}
|
|
41278
|
+
context.emit("text", `
|
|
41279
|
+
Reason: ${match.reason}
|
|
41280
|
+
`);
|
|
41281
|
+
if (match.suggestions && match.suggestions.length > 0) {
|
|
41282
|
+
context.emit("text", `
|
|
41283
|
+
Suggestions:
|
|
41284
|
+
`);
|
|
41285
|
+
for (const suggestion of match.suggestions) {
|
|
41286
|
+
context.emit("text", ` \u2022 ${suggestion}
|
|
41287
|
+
`);
|
|
41288
|
+
}
|
|
41289
|
+
}
|
|
41290
|
+
context.emit("done");
|
|
41291
|
+
return { handled: true };
|
|
41292
|
+
}
|
|
41293
|
+
};
|
|
41294
|
+
}
|
|
40645
41295
|
exitCommand() {
|
|
40646
41296
|
return {
|
|
40647
41297
|
name: "exit",
|
|
@@ -41608,9 +42258,9 @@ Format the summary as a brief bullet-point list. This summary will replace the c
|
|
|
41608
42258
|
content: "",
|
|
41609
42259
|
handler: async (args, context) => {
|
|
41610
42260
|
const configPaths = [
|
|
41611
|
-
|
|
41612
|
-
|
|
41613
|
-
|
|
42261
|
+
join12(context.cwd, ".assistants", "config.json"),
|
|
42262
|
+
join12(context.cwd, ".assistants", "config.local.json"),
|
|
42263
|
+
join12(getConfigDir(), "config.json")
|
|
41614
42264
|
];
|
|
41615
42265
|
let message = `
|
|
41616
42266
|
**Configuration**
|
|
@@ -41628,9 +42278,9 @@ Format the summary as a brief bullet-point list. This summary will replace the c
|
|
|
41628
42278
|
message += `
|
|
41629
42279
|
**Commands Directories:**
|
|
41630
42280
|
`;
|
|
41631
|
-
message += ` - Project: ${
|
|
42281
|
+
message += ` - Project: ${join12(context.cwd, ".assistants", "commands")}
|
|
41632
42282
|
`;
|
|
41633
|
-
message += ` - Global: ${
|
|
42283
|
+
message += ` - Global: ${join12(homeDir, ".assistants", "commands")}
|
|
41634
42284
|
`;
|
|
41635
42285
|
context.emit("text", message);
|
|
41636
42286
|
context.emit("done");
|
|
@@ -41646,8 +42296,8 @@ Format the summary as a brief bullet-point list. This summary will replace the c
|
|
|
41646
42296
|
selfHandled: true,
|
|
41647
42297
|
content: "",
|
|
41648
42298
|
handler: async (args, context) => {
|
|
41649
|
-
const commandsDir =
|
|
41650
|
-
|
|
42299
|
+
const commandsDir = join12(context.cwd, ".assistants", "commands");
|
|
42300
|
+
mkdirSync4(commandsDir, { recursive: true });
|
|
41651
42301
|
const exampleCommand = `---
|
|
41652
42302
|
name: reflect
|
|
41653
42303
|
description: Reflect on the conversation and suggest next steps
|
|
@@ -41662,9 +42312,9 @@ Please summarize the last interaction and suggest 2-3 next steps.
|
|
|
41662
42312
|
- Focus on clarity
|
|
41663
42313
|
- Ask a follow-up question if needed
|
|
41664
42314
|
`;
|
|
41665
|
-
const examplePath =
|
|
42315
|
+
const examplePath = join12(commandsDir, "reflect.md");
|
|
41666
42316
|
if (!existsSync7(examplePath)) {
|
|
41667
|
-
|
|
42317
|
+
writeFileSync5(examplePath, exampleCommand);
|
|
41668
42318
|
}
|
|
41669
42319
|
let message = `
|
|
41670
42320
|
**Initialized assistants**
|
|
@@ -41900,8 +42550,19 @@ Keep it concise but comprehensive.`
|
|
|
41900
42550
|
handler: async (args, context) => {
|
|
41901
42551
|
const id = args.trim();
|
|
41902
42552
|
if (!id) {
|
|
41903
|
-
|
|
42553
|
+
const schedules = await listSchedules(context.cwd);
|
|
42554
|
+
if (schedules.length === 0) {
|
|
42555
|
+
context.emit("text", `No schedules found.
|
|
42556
|
+
`);
|
|
42557
|
+
} else {
|
|
42558
|
+
const lines = schedules.sort((a, b) => (a.nextRunAt || 0) - (b.nextRunAt || 0)).map((schedule) => `- ${schedule.id} [${schedule.status}] ${schedule.command}`);
|
|
42559
|
+
context.emit("text", `Usage: /pause <id>
|
|
42560
|
+
|
|
42561
|
+
Available schedules:
|
|
42562
|
+
${lines.join(`
|
|
42563
|
+
`)}
|
|
41904
42564
|
`);
|
|
42565
|
+
}
|
|
41905
42566
|
context.emit("done");
|
|
41906
42567
|
return { handled: true };
|
|
41907
42568
|
}
|
|
@@ -41927,8 +42588,19 @@ Keep it concise but comprehensive.`
|
|
|
41927
42588
|
handler: async (args, context) => {
|
|
41928
42589
|
const id = args.trim();
|
|
41929
42590
|
if (!id) {
|
|
41930
|
-
|
|
42591
|
+
const schedules = await listSchedules(context.cwd);
|
|
42592
|
+
if (schedules.length === 0) {
|
|
42593
|
+
context.emit("text", `No schedules found.
|
|
42594
|
+
`);
|
|
42595
|
+
} else {
|
|
42596
|
+
const lines = schedules.sort((a, b) => (a.nextRunAt || 0) - (b.nextRunAt || 0)).map((schedule) => `- ${schedule.id} [${schedule.status}] ${schedule.command}`);
|
|
42597
|
+
context.emit("text", `Usage: /resume <id>
|
|
42598
|
+
|
|
42599
|
+
Available schedules:
|
|
42600
|
+
${lines.join(`
|
|
42601
|
+
`)}
|
|
41931
42602
|
`);
|
|
42603
|
+
}
|
|
41932
42604
|
context.emit("done");
|
|
41933
42605
|
return { handled: true };
|
|
41934
42606
|
}
|
|
@@ -42363,7 +43035,7 @@ async function createLLMClient(config) {
|
|
|
42363
43035
|
|
|
42364
43036
|
// packages/core/src/heartbeat/manager.ts
|
|
42365
43037
|
import { dirname as dirname5 } from "path";
|
|
42366
|
-
import { mkdirSync as
|
|
43038
|
+
import { mkdirSync as mkdirSync5 } from "fs";
|
|
42367
43039
|
import { readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
42368
43040
|
|
|
42369
43041
|
class HeartbeatManager {
|
|
@@ -42385,7 +43057,7 @@ class HeartbeatManager {
|
|
|
42385
43057
|
uptimeSeconds: 0
|
|
42386
43058
|
};
|
|
42387
43059
|
const dir = dirname5(config.persistPath);
|
|
42388
|
-
|
|
43060
|
+
mkdirSync5(dir, { recursive: true });
|
|
42389
43061
|
}
|
|
42390
43062
|
start(sessionId) {
|
|
42391
43063
|
if (this.intervalId)
|
|
@@ -42459,14 +43131,14 @@ class HeartbeatManager {
|
|
|
42459
43131
|
}
|
|
42460
43132
|
// packages/core/src/heartbeat/persistence.ts
|
|
42461
43133
|
import { dirname as dirname6 } from "path";
|
|
42462
|
-
import { mkdirSync as
|
|
43134
|
+
import { mkdirSync as mkdirSync6 } from "fs";
|
|
42463
43135
|
import { readFile as readFile5, writeFile as writeFile4, unlink as unlink3 } from "fs/promises";
|
|
42464
43136
|
|
|
42465
43137
|
class StatePersistence {
|
|
42466
43138
|
path;
|
|
42467
43139
|
constructor(path2) {
|
|
42468
43140
|
this.path = path2;
|
|
42469
|
-
|
|
43141
|
+
mkdirSync6(dirname6(path2), { recursive: true });
|
|
42470
43142
|
}
|
|
42471
43143
|
async save(state) {
|
|
42472
43144
|
try {
|
|
@@ -42686,14 +43358,14 @@ class EnergyManager {
|
|
|
42686
43358
|
}
|
|
42687
43359
|
// packages/core/src/energy/storage.ts
|
|
42688
43360
|
import { dirname as dirname7 } from "path";
|
|
42689
|
-
import { mkdirSync as
|
|
43361
|
+
import { mkdirSync as mkdirSync7 } from "fs";
|
|
42690
43362
|
import { readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
42691
43363
|
|
|
42692
43364
|
class EnergyStorage {
|
|
42693
43365
|
path;
|
|
42694
43366
|
constructor(path2) {
|
|
42695
43367
|
this.path = path2;
|
|
42696
|
-
|
|
43368
|
+
mkdirSync7(dirname7(path2), { recursive: true });
|
|
42697
43369
|
}
|
|
42698
43370
|
async save(state) {
|
|
42699
43371
|
try {
|
|
@@ -42760,18 +43432,18 @@ function validateToolCalls(toolCalls, tools) {
|
|
|
42760
43432
|
}
|
|
42761
43433
|
|
|
42762
43434
|
// packages/core/src/voice/utils.ts
|
|
42763
|
-
import { existsSync as existsSync9, readFileSync as
|
|
43435
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
|
|
42764
43436
|
import { homedir as homedir9 } from "os";
|
|
42765
|
-
import { join as
|
|
43437
|
+
import { join as join14 } from "path";
|
|
42766
43438
|
import { spawnSync } from "child_process";
|
|
42767
43439
|
function loadApiKeyFromSecrets2(key) {
|
|
42768
43440
|
const envHome = process.env.HOME || process.env.USERPROFILE;
|
|
42769
43441
|
const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir9();
|
|
42770
|
-
const secretsPath =
|
|
43442
|
+
const secretsPath = join14(homeDir, ".secrets");
|
|
42771
43443
|
if (!existsSync9(secretsPath))
|
|
42772
43444
|
return;
|
|
42773
43445
|
try {
|
|
42774
|
-
const content =
|
|
43446
|
+
const content = readFileSync5(secretsPath, "utf-8");
|
|
42775
43447
|
const match = content.match(new RegExp(`export\\s+${key}\\s*=\\s*['"]?([^'"\\n]+)['"]?`));
|
|
42776
43448
|
return match?.[1];
|
|
42777
43449
|
} catch {
|
|
@@ -42841,8 +43513,8 @@ class SystemSTT {
|
|
|
42841
43513
|
// packages/core/src/voice/tts.ts
|
|
42842
43514
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
42843
43515
|
import { tmpdir as tmpdir2 } from "os";
|
|
42844
|
-
import { join as
|
|
42845
|
-
import { readFileSync as
|
|
43516
|
+
import { join as join15 } from "path";
|
|
43517
|
+
import { readFileSync as readFileSync6, unlinkSync as unlinkSync2 } from "fs";
|
|
42846
43518
|
class ElevenLabsTTS {
|
|
42847
43519
|
apiKey;
|
|
42848
43520
|
voiceId;
|
|
@@ -42945,7 +43617,7 @@ class SystemTTS {
|
|
|
42945
43617
|
if (!say) {
|
|
42946
43618
|
throw new Error('System TTS not available: missing "say" command.');
|
|
42947
43619
|
}
|
|
42948
|
-
const output =
|
|
43620
|
+
const output = join15(tmpdir2(), `assistants-tts-${Date.now()}.aiff`);
|
|
42949
43621
|
const args = [];
|
|
42950
43622
|
if (this.voiceId) {
|
|
42951
43623
|
args.push("-v", this.voiceId);
|
|
@@ -42958,7 +43630,7 @@ class SystemTTS {
|
|
|
42958
43630
|
if (result.status !== 0) {
|
|
42959
43631
|
throw new Error(`System TTS failed: ${result.stderr || "unknown error"}`);
|
|
42960
43632
|
}
|
|
42961
|
-
const audio =
|
|
43633
|
+
const audio = readFileSync6(output);
|
|
42962
43634
|
unlinkSync2(output);
|
|
42963
43635
|
return {
|
|
42964
43636
|
audio: audio.buffer.slice(audio.byteOffset, audio.byteOffset + audio.byteLength),
|
|
@@ -42967,7 +43639,7 @@ class SystemTTS {
|
|
|
42967
43639
|
}
|
|
42968
43640
|
const espeak = findExecutable("espeak") || findExecutable("espeak-ng");
|
|
42969
43641
|
if (espeak) {
|
|
42970
|
-
const output =
|
|
43642
|
+
const output = join15(tmpdir2(), `assistants-tts-${Date.now()}.wav`);
|
|
42971
43643
|
const args = ["-w", output];
|
|
42972
43644
|
if (this.voiceId) {
|
|
42973
43645
|
args.push("-v", this.voiceId);
|
|
@@ -42980,7 +43652,7 @@ class SystemTTS {
|
|
|
42980
43652
|
if (result.status !== 0) {
|
|
42981
43653
|
throw new Error(`System TTS failed: ${result.stderr || "unknown error"}`);
|
|
42982
43654
|
}
|
|
42983
|
-
const audio =
|
|
43655
|
+
const audio = readFileSync6(output);
|
|
42984
43656
|
unlinkSync2(output);
|
|
42985
43657
|
return {
|
|
42986
43658
|
audio: audio.buffer.slice(audio.byteOffset, audio.byteOffset + audio.byteLength),
|
|
@@ -42994,15 +43666,15 @@ class SystemTTS {
|
|
|
42994
43666
|
// packages/core/src/voice/player.ts
|
|
42995
43667
|
import { spawn } from "child_process";
|
|
42996
43668
|
import { tmpdir as tmpdir3 } from "os";
|
|
42997
|
-
import { join as
|
|
42998
|
-
import { unlink as unlink4, writeFileSync as
|
|
43669
|
+
import { join as join16 } from "path";
|
|
43670
|
+
import { unlink as unlink4, writeFileSync as writeFileSync6 } from "fs";
|
|
42999
43671
|
class AudioPlayer {
|
|
43000
43672
|
currentProcess = null;
|
|
43001
43673
|
playing = false;
|
|
43002
43674
|
async play(audio, options = {}) {
|
|
43003
43675
|
const format = options.format ?? "mp3";
|
|
43004
|
-
const tempFile =
|
|
43005
|
-
|
|
43676
|
+
const tempFile = join16(tmpdir3(), `assistants-audio-${Date.now()}.${format}`);
|
|
43677
|
+
writeFileSync6(tempFile, Buffer.from(audio));
|
|
43006
43678
|
const player = this.resolvePlayer(format);
|
|
43007
43679
|
if (!player) {
|
|
43008
43680
|
throw new Error("No supported audio player found. Install afplay, ffplay, mpg123, or aplay.");
|
|
@@ -43069,8 +43741,8 @@ class AudioPlayer {
|
|
|
43069
43741
|
// packages/core/src/voice/recorder.ts
|
|
43070
43742
|
import { spawn as spawn2 } from "child_process";
|
|
43071
43743
|
import { tmpdir as tmpdir4 } from "os";
|
|
43072
|
-
import { join as
|
|
43073
|
-
import { readFileSync as
|
|
43744
|
+
import { join as join17 } from "path";
|
|
43745
|
+
import { readFileSync as readFileSync7, unlink as unlink5 } from "fs";
|
|
43074
43746
|
class AudioRecorder {
|
|
43075
43747
|
currentProcess = null;
|
|
43076
43748
|
async record(options = {}) {
|
|
@@ -43080,7 +43752,7 @@ class AudioRecorder {
|
|
|
43080
43752
|
const duration = options.durationSeconds ?? 5;
|
|
43081
43753
|
const sampleRate = options.sampleRate ?? 16000;
|
|
43082
43754
|
const channels = options.channels ?? 1;
|
|
43083
|
-
const output =
|
|
43755
|
+
const output = join17(tmpdir4(), `assistants-record-${Date.now()}.wav`);
|
|
43084
43756
|
const recorder = this.resolveRecorder(sampleRate, channels, duration, output);
|
|
43085
43757
|
if (!recorder) {
|
|
43086
43758
|
throw new Error("No supported audio recorder found. Install sox or ffmpeg.");
|
|
@@ -43100,7 +43772,7 @@ class AudioRecorder {
|
|
|
43100
43772
|
reject(error);
|
|
43101
43773
|
});
|
|
43102
43774
|
});
|
|
43103
|
-
const data =
|
|
43775
|
+
const data = readFileSync7(output);
|
|
43104
43776
|
unlink5(output, () => {});
|
|
43105
43777
|
return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
43106
43778
|
}
|
|
@@ -43274,13 +43946,13 @@ class VoiceManager {
|
|
|
43274
43946
|
init_src();
|
|
43275
43947
|
import { existsSync as existsSync11 } from "fs";
|
|
43276
43948
|
import { mkdir as mkdir5, readFile as readFile8, writeFile as writeFile7, rm as rm2 } from "fs/promises";
|
|
43277
|
-
import { join as
|
|
43949
|
+
import { join as join19 } from "path";
|
|
43278
43950
|
|
|
43279
43951
|
// packages/core/src/identity/identity-manager.ts
|
|
43280
43952
|
init_src();
|
|
43281
43953
|
import { existsSync as existsSync10 } from "fs";
|
|
43282
43954
|
import { mkdir as mkdir4, readFile as readFile7, writeFile as writeFile6, rm } from "fs/promises";
|
|
43283
|
-
import { join as
|
|
43955
|
+
import { join as join18 } from "path";
|
|
43284
43956
|
var DEFAULT_PROFILE = {
|
|
43285
43957
|
displayName: "Assistant",
|
|
43286
43958
|
timezone: "UTC",
|
|
@@ -43310,19 +43982,19 @@ class IdentityManager {
|
|
|
43310
43982
|
this.basePath = basePath;
|
|
43311
43983
|
}
|
|
43312
43984
|
get identitiesRoot() {
|
|
43313
|
-
return
|
|
43985
|
+
return join18(this.basePath, "assistants", this.assistantId, "identities");
|
|
43314
43986
|
}
|
|
43315
43987
|
get indexPath() {
|
|
43316
|
-
return
|
|
43988
|
+
return join18(this.identitiesRoot, "index.json");
|
|
43317
43989
|
}
|
|
43318
43990
|
get activePath() {
|
|
43319
|
-
return
|
|
43991
|
+
return join18(this.identitiesRoot, "active.json");
|
|
43320
43992
|
}
|
|
43321
43993
|
identityPath(id) {
|
|
43322
|
-
return
|
|
43994
|
+
return join18(this.identitiesRoot, `${id}.json`);
|
|
43323
43995
|
}
|
|
43324
43996
|
assistantConfigPath() {
|
|
43325
|
-
return
|
|
43997
|
+
return join18(this.basePath, "assistants", this.assistantId, "config.json");
|
|
43326
43998
|
}
|
|
43327
43999
|
async initialize() {
|
|
43328
44000
|
await mkdir4(this.identitiesRoot, { recursive: true });
|
|
@@ -43506,16 +44178,16 @@ class AssistantManager {
|
|
|
43506
44178
|
this.basePath = basePath;
|
|
43507
44179
|
}
|
|
43508
44180
|
get assistantsRoot() {
|
|
43509
|
-
return
|
|
44181
|
+
return join19(this.basePath, "assistants");
|
|
43510
44182
|
}
|
|
43511
44183
|
get indexPath() {
|
|
43512
|
-
return
|
|
44184
|
+
return join19(this.assistantsRoot, "index.json");
|
|
43513
44185
|
}
|
|
43514
44186
|
get activePath() {
|
|
43515
|
-
return
|
|
44187
|
+
return join19(this.basePath, "active.json");
|
|
43516
44188
|
}
|
|
43517
44189
|
assistantConfigPath(id) {
|
|
43518
|
-
return
|
|
44190
|
+
return join19(this.assistantsRoot, id, "config.json");
|
|
43519
44191
|
}
|
|
43520
44192
|
async initialize() {
|
|
43521
44193
|
await mkdir5(this.assistantsRoot, { recursive: true });
|
|
@@ -43569,7 +44241,7 @@ class AssistantManager {
|
|
|
43569
44241
|
if (!this.assistants.has(id)) {
|
|
43570
44242
|
throw new Error(`Assistant ${id} not found`);
|
|
43571
44243
|
}
|
|
43572
|
-
await rm2(
|
|
44244
|
+
await rm2(join19(this.assistantsRoot, id), { recursive: true, force: true });
|
|
43573
44245
|
this.assistants.delete(id);
|
|
43574
44246
|
await this.removeFromIndex(id);
|
|
43575
44247
|
if (this.activeId === id) {
|
|
@@ -43635,7 +44307,7 @@ class AssistantManager {
|
|
|
43635
44307
|
}
|
|
43636
44308
|
}
|
|
43637
44309
|
async persistAssistant(assistant) {
|
|
43638
|
-
const dir =
|
|
44310
|
+
const dir = join19(this.assistantsRoot, assistant.id);
|
|
43639
44311
|
await mkdir5(dir, { recursive: true });
|
|
43640
44312
|
await writeFile7(this.assistantConfigPath(assistant.id), JSON.stringify(assistant, null, 2));
|
|
43641
44313
|
}
|
|
@@ -43675,6 +44347,7 @@ class AgentLoop {
|
|
|
43675
44347
|
skillExecutor;
|
|
43676
44348
|
hookLoader;
|
|
43677
44349
|
hookExecutor;
|
|
44350
|
+
scopeContextManager;
|
|
43678
44351
|
commandLoader;
|
|
43679
44352
|
commandExecutor;
|
|
43680
44353
|
builtinCommands;
|
|
@@ -43716,6 +44389,7 @@ class AgentLoop {
|
|
|
43716
44389
|
this.skillExecutor = new SkillExecutor;
|
|
43717
44390
|
this.hookLoader = new HookLoader;
|
|
43718
44391
|
this.hookExecutor = new HookExecutor;
|
|
44392
|
+
this.scopeContextManager = new ScopeContextManager;
|
|
43719
44393
|
this.commandLoader = new CommandLoader(this.cwd);
|
|
43720
44394
|
this.commandExecutor = new CommandExecutor(this.commandLoader);
|
|
43721
44395
|
this.builtinCommands = new BuiltinCommands;
|
|
@@ -43781,6 +44455,14 @@ class AgentLoop {
|
|
|
43781
44455
|
this.connectorBridge.registerAll(this.toolRegistry);
|
|
43782
44456
|
this.builtinCommands.registerAll(this.commandLoader);
|
|
43783
44457
|
this.hookLoader.load(hooksConfig);
|
|
44458
|
+
nativeHookRegistry.register(createScopeVerificationHook());
|
|
44459
|
+
const nativeConfig = hooksConfig?.native;
|
|
44460
|
+
if (nativeConfig) {
|
|
44461
|
+
nativeHookRegistry.setConfig(nativeConfig);
|
|
44462
|
+
if (nativeConfig.scopeVerification) {
|
|
44463
|
+
this.scopeContextManager.setConfig(nativeConfig.scopeVerification);
|
|
44464
|
+
}
|
|
44465
|
+
}
|
|
43784
44466
|
this.hookExecutor.setAgentRunner((hook, input, timeout) => runHookAgent({ hook, input, timeout, cwd: this.cwd }));
|
|
43785
44467
|
this.systemPrompt = systemPrompt || null;
|
|
43786
44468
|
if (this.systemPrompt) {
|
|
@@ -43829,6 +44511,10 @@ class AgentLoop {
|
|
|
43829
44511
|
return { ok: false, error: promptHookResult.stopReason || "Blocked by hook" };
|
|
43830
44512
|
}
|
|
43831
44513
|
}
|
|
44514
|
+
const explicitToolResult = await this.handleExplicitToolCommand(userMessage);
|
|
44515
|
+
if (explicitToolResult) {
|
|
44516
|
+
return explicitToolResult;
|
|
44517
|
+
}
|
|
43832
44518
|
if (userMessage.startsWith("/")) {
|
|
43833
44519
|
const parsed = this.commandExecutor.parseCommand(userMessage);
|
|
43834
44520
|
const command = parsed ? this.commandLoader.getCommand(parsed.name) : undefined;
|
|
@@ -43863,6 +44549,12 @@ class AgentLoop {
|
|
|
43863
44549
|
}
|
|
43864
44550
|
const limits = getLimits();
|
|
43865
44551
|
userMessage = enforceMessageLimit(userMessage, limits.maxUserMessageLength);
|
|
44552
|
+
if (source === "user") {
|
|
44553
|
+
const scopeContext = await this.scopeContextManager.createContext(userMessage, this.llmClient);
|
|
44554
|
+
if (scopeContext) {
|
|
44555
|
+
this.context.setScopeContext(scopeContext);
|
|
44556
|
+
}
|
|
44557
|
+
}
|
|
43866
44558
|
this.context.addUserMessage(userMessage);
|
|
43867
44559
|
await this.runLoop();
|
|
43868
44560
|
this.contextManager?.refreshState(this.context.getMessages());
|
|
@@ -43880,6 +44572,47 @@ class AgentLoop {
|
|
|
43880
44572
|
this.drainScheduledQueue();
|
|
43881
44573
|
}
|
|
43882
44574
|
}
|
|
44575
|
+
async handleExplicitToolCommand(userMessage) {
|
|
44576
|
+
const match = userMessage.match(/^!\[(\w+)\]\s*([\s\S]*)$/);
|
|
44577
|
+
if (!match)
|
|
44578
|
+
return null;
|
|
44579
|
+
const toolName = match[1].toLowerCase();
|
|
44580
|
+
const command = match[2].trim();
|
|
44581
|
+
if (!command) {
|
|
44582
|
+
this.emit({ type: "text", content: `Usage: ![${toolName}] <command>
|
|
44583
|
+
` });
|
|
44584
|
+
this.emit({ type: "done" });
|
|
44585
|
+
return { ok: false, error: "Missing command" };
|
|
44586
|
+
}
|
|
44587
|
+
if (toolName !== "bash") {
|
|
44588
|
+
this.emit({ type: "text", content: `Unsupported tool: ${toolName}
|
|
44589
|
+
` });
|
|
44590
|
+
this.emit({ type: "done" });
|
|
44591
|
+
return { ok: false, error: "Unsupported tool" };
|
|
44592
|
+
}
|
|
44593
|
+
const toolCall = {
|
|
44594
|
+
id: generateId(),
|
|
44595
|
+
name: "bash",
|
|
44596
|
+
type: "tool",
|
|
44597
|
+
input: {
|
|
44598
|
+
command,
|
|
44599
|
+
cwd: this.cwd,
|
|
44600
|
+
sessionId: this.sessionId
|
|
44601
|
+
}
|
|
44602
|
+
};
|
|
44603
|
+
this.context.addUserMessage(userMessage);
|
|
44604
|
+
this.context.addAssistantMessage("", [toolCall]);
|
|
44605
|
+
this.emit({ type: "tool_use", toolCall });
|
|
44606
|
+
const results = await this.executeToolCalls([toolCall]);
|
|
44607
|
+
this.context.addToolResults(results);
|
|
44608
|
+
this.emit({ type: "done" });
|
|
44609
|
+
const failed = results.some((result) => result.isError);
|
|
44610
|
+
if (failed) {
|
|
44611
|
+
const error = results.find((result) => result.isError)?.content;
|
|
44612
|
+
return { ok: false, error: error ? String(error) : "Tool execution failed" };
|
|
44613
|
+
}
|
|
44614
|
+
return { ok: true, summary: `Executed ${toolName}` };
|
|
44615
|
+
}
|
|
43883
44616
|
async runLoop() {
|
|
43884
44617
|
const maxTurns = 50;
|
|
43885
44618
|
let turn = 0;
|
|
@@ -43945,6 +44678,21 @@ class AgentLoop {
|
|
|
43945
44678
|
hook_event_name: "Stop",
|
|
43946
44679
|
cwd: this.cwd
|
|
43947
44680
|
});
|
|
44681
|
+
const verificationResult = await this.runScopeVerification();
|
|
44682
|
+
if (verificationResult && verificationResult.continue === false) {
|
|
44683
|
+
if (verificationResult.systemMessage) {
|
|
44684
|
+
this.context.addSystemMessage(verificationResult.systemMessage);
|
|
44685
|
+
}
|
|
44686
|
+
this.scopeContextManager.incrementAttempts();
|
|
44687
|
+
const scope = this.scopeContextManager.getContext();
|
|
44688
|
+
if (scope) {
|
|
44689
|
+
this.context.setScopeContext(scope);
|
|
44690
|
+
}
|
|
44691
|
+
await this.runLoop();
|
|
44692
|
+
return;
|
|
44693
|
+
}
|
|
44694
|
+
this.scopeContextManager.clear();
|
|
44695
|
+
this.context.clearScopeContext();
|
|
43948
44696
|
this.emit({ type: "done" });
|
|
43949
44697
|
}
|
|
43950
44698
|
if (streamError) {
|
|
@@ -43976,6 +44724,36 @@ class AgentLoop {
|
|
|
43976
44724
|
` });
|
|
43977
44725
|
}
|
|
43978
44726
|
}
|
|
44727
|
+
async runScopeVerification() {
|
|
44728
|
+
if (!this.scopeContextManager.isEnabled()) {
|
|
44729
|
+
return null;
|
|
44730
|
+
}
|
|
44731
|
+
if (this.scopeContextManager.hasReachedMaxAttempts()) {
|
|
44732
|
+
return null;
|
|
44733
|
+
}
|
|
44734
|
+
const scopeContext = this.context.getScopeContext();
|
|
44735
|
+
if (!scopeContext) {
|
|
44736
|
+
return null;
|
|
44737
|
+
}
|
|
44738
|
+
const result = await nativeHookRegistry.execute("Stop", {
|
|
44739
|
+
session_id: this.sessionId,
|
|
44740
|
+
hook_event_name: "Stop",
|
|
44741
|
+
cwd: this.cwd
|
|
44742
|
+
}, {
|
|
44743
|
+
sessionId: this.sessionId,
|
|
44744
|
+
cwd: this.cwd,
|
|
44745
|
+
messages: this.context.getMessages(),
|
|
44746
|
+
scopeContext,
|
|
44747
|
+
llmClient: this.llmClient
|
|
44748
|
+
});
|
|
44749
|
+
if (!result) {
|
|
44750
|
+
return null;
|
|
44751
|
+
}
|
|
44752
|
+
return {
|
|
44753
|
+
continue: result.continue !== false,
|
|
44754
|
+
systemMessage: result.systemMessage
|
|
44755
|
+
};
|
|
44756
|
+
}
|
|
43979
44757
|
async executeToolCalls(toolCalls) {
|
|
43980
44758
|
const results = [];
|
|
43981
44759
|
for (const toolCall of toolCalls) {
|
|
@@ -44365,7 +45143,7 @@ ${content.trim()}`);
|
|
|
44365
45143
|
const heartbeatConfig = this.buildHeartbeatConfig(this.config);
|
|
44366
45144
|
if (!heartbeatConfig)
|
|
44367
45145
|
return;
|
|
44368
|
-
const statePath =
|
|
45146
|
+
const statePath = join20(getConfigDir(), "state", `${this.sessionId}.json`);
|
|
44369
45147
|
this.heartbeatManager = new HeartbeatManager(heartbeatConfig);
|
|
44370
45148
|
this.heartbeatPersistence = new StatePersistence(statePath);
|
|
44371
45149
|
this.heartbeatRecovery = new RecoveryManager(this.heartbeatPersistence, heartbeatConfig.persistPath, heartbeatConfig.staleThresholdMs, {
|
|
@@ -44414,7 +45192,7 @@ ${content.trim()}`);
|
|
|
44414
45192
|
async startEnergySystem() {
|
|
44415
45193
|
if (!this.config || this.config.energy?.enabled === false)
|
|
44416
45194
|
return;
|
|
44417
|
-
const statePath =
|
|
45195
|
+
const statePath = join20(getConfigDir(), "energy", "state.json");
|
|
44418
45196
|
this.energyManager = new EnergyManager(this.config.energy, new EnergyStorage(statePath));
|
|
44419
45197
|
await this.energyManager.initialize();
|
|
44420
45198
|
this.refreshEnergyEffects();
|
|
@@ -44481,7 +45259,24 @@ ${effects.message}
|
|
|
44481
45259
|
await releaseScheduleLock(this.cwd, schedule.id, this.sessionId);
|
|
44482
45260
|
continue;
|
|
44483
45261
|
}
|
|
44484
|
-
|
|
45262
|
+
let scheduleToQueue = schedule;
|
|
45263
|
+
if (!schedule.sessionId) {
|
|
45264
|
+
const claimed = await updateSchedule(this.cwd, schedule.id, (current) => {
|
|
45265
|
+
if (current.sessionId)
|
|
45266
|
+
return current;
|
|
45267
|
+
return {
|
|
45268
|
+
...current,
|
|
45269
|
+
sessionId: this.sessionId,
|
|
45270
|
+
updatedAt: Date.now()
|
|
45271
|
+
};
|
|
45272
|
+
});
|
|
45273
|
+
if (!claimed || claimed.sessionId && claimed.sessionId !== this.sessionId) {
|
|
45274
|
+
await releaseScheduleLock(this.cwd, schedule.id, this.sessionId);
|
|
45275
|
+
continue;
|
|
45276
|
+
}
|
|
45277
|
+
scheduleToQueue = claimed;
|
|
45278
|
+
}
|
|
45279
|
+
this.scheduledQueue.push(scheduleToQueue);
|
|
44485
45280
|
}
|
|
44486
45281
|
this.drainScheduledQueue();
|
|
44487
45282
|
} catch (error) {
|
|
@@ -44643,7 +45438,7 @@ ${this.identityContext}`);
|
|
|
44643
45438
|
return null;
|
|
44644
45439
|
const intervalMs = Math.max(1000, config.heartbeat?.intervalMs ?? 15000);
|
|
44645
45440
|
const staleThresholdMs = Math.max(intervalMs * 2, config.heartbeat?.staleThresholdMs ?? 120000);
|
|
44646
|
-
const persistPath = config.heartbeat?.persistPath ??
|
|
45441
|
+
const persistPath = config.heartbeat?.persistPath ?? join20(getConfigDir(), "heartbeats", `${this.sessionId}.json`);
|
|
44647
45442
|
return {
|
|
44648
45443
|
intervalMs,
|
|
44649
45444
|
staleThresholdMs,
|
|
@@ -44733,116 +45528,13 @@ function parseErrorCode(message) {
|
|
|
44733
45528
|
}
|
|
44734
45529
|
// packages/core/src/memory/sessions.ts
|
|
44735
45530
|
init_src();
|
|
44736
|
-
// packages/core/src/migration/migrate-to-assistants.ts
|
|
44737
|
-
import { existsSync as existsSync12 } from "fs";
|
|
44738
|
-
import { mkdir as mkdir6, readFile as readFile9, writeFile as writeFile8, rename, cp } from "fs/promises";
|
|
44739
|
-
import { join as join20 } from "path";
|
|
44740
|
-
import { homedir as homedir10 } from "os";
|
|
44741
|
-
var MIGRATION_MARKER = ".migrated-from-oldpal";
|
|
44742
|
-
async function ensureDir(path2) {
|
|
44743
|
-
await mkdir6(path2, { recursive: true });
|
|
44744
|
-
}
|
|
44745
|
-
async function copyIfExists(source, destination) {
|
|
44746
|
-
if (!existsSync12(source))
|
|
44747
|
-
return false;
|
|
44748
|
-
await ensureDir(join20(destination, ".."));
|
|
44749
|
-
await cp(source, destination, { recursive: true });
|
|
44750
|
-
return true;
|
|
44751
|
-
}
|
|
44752
|
-
async function readJson(path2) {
|
|
44753
|
-
if (!existsSync12(path2))
|
|
44754
|
-
return null;
|
|
44755
|
-
try {
|
|
44756
|
-
const raw = await readFile9(path2, "utf-8");
|
|
44757
|
-
return JSON.parse(raw);
|
|
44758
|
-
} catch {
|
|
44759
|
-
return null;
|
|
44760
|
-
}
|
|
44761
|
-
}
|
|
44762
|
-
async function migrateFromOldpal() {
|
|
44763
|
-
const result = {
|
|
44764
|
-
success: false,
|
|
44765
|
-
migrated: [],
|
|
44766
|
-
errors: []
|
|
44767
|
-
};
|
|
44768
|
-
const home = homedir10();
|
|
44769
|
-
const oldPath = join20(home, ".oldpal");
|
|
44770
|
-
const newPath = join20(home, ".assistants");
|
|
44771
|
-
if (!existsSync12(oldPath)) {
|
|
44772
|
-
result.success = true;
|
|
44773
|
-
return result;
|
|
44774
|
-
}
|
|
44775
|
-
if (existsSync12(newPath)) {
|
|
44776
|
-
const marker = join20(newPath, "migration", MIGRATION_MARKER);
|
|
44777
|
-
if (existsSync12(marker)) {
|
|
44778
|
-
result.success = true;
|
|
44779
|
-
return result;
|
|
44780
|
-
}
|
|
44781
|
-
result.errors.push("Both ~/.oldpal and ~/.assistants exist. Manual merge required.");
|
|
44782
|
-
return result;
|
|
44783
|
-
}
|
|
44784
|
-
try {
|
|
44785
|
-
await ensureDir(newPath);
|
|
44786
|
-
await ensureDir(join20(newPath, "assistants"));
|
|
44787
|
-
await ensureDir(join20(newPath, "shared", "skills"));
|
|
44788
|
-
await ensureDir(join20(newPath, "logs"));
|
|
44789
|
-
await ensureDir(join20(newPath, "migration"));
|
|
44790
|
-
const config = await readJson(join20(oldPath, "settings.json"));
|
|
44791
|
-
if (config) {
|
|
44792
|
-
await writeFile8(join20(newPath, "config.json"), JSON.stringify(config, null, 2));
|
|
44793
|
-
result.migrated.push("config.json");
|
|
44794
|
-
}
|
|
44795
|
-
if (await copyIfExists(join20(oldPath, "settings.local.json"), join20(newPath, "config.local.json"))) {
|
|
44796
|
-
result.migrated.push("config.local.json");
|
|
44797
|
-
}
|
|
44798
|
-
if (await copyIfExists(join20(oldPath, "hooks.json"), join20(newPath, "hooks.json"))) {
|
|
44799
|
-
result.migrated.push("hooks.json");
|
|
44800
|
-
}
|
|
44801
|
-
if (await copyIfExists(join20(oldPath, "commands"), join20(newPath, "commands"))) {
|
|
44802
|
-
result.migrated.push("commands");
|
|
44803
|
-
}
|
|
44804
|
-
if (await copyIfExists(join20(oldPath, "OLDPAL.md"), join20(newPath, "ASSISTANTS.md"))) {
|
|
44805
|
-
result.migrated.push("ASSISTANTS.md");
|
|
44806
|
-
}
|
|
44807
|
-
if (await copyIfExists(join20(oldPath, "skills"), join20(newPath, "shared", "skills"))) {
|
|
44808
|
-
result.migrated.push("skills");
|
|
44809
|
-
}
|
|
44810
|
-
if (await copyIfExists(join20(oldPath, "logs"), join20(newPath, "logs"))) {
|
|
44811
|
-
result.migrated.push("logs");
|
|
44812
|
-
}
|
|
44813
|
-
const manager = new AssistantManager(newPath);
|
|
44814
|
-
await manager.initialize();
|
|
44815
|
-
const assistant = await manager.createAssistant({
|
|
44816
|
-
name: config?.llm?.model ? `Assistant (${config.llm.model})` : "Default Assistant",
|
|
44817
|
-
settings: config?.llm ? {
|
|
44818
|
-
model: config.llm.model,
|
|
44819
|
-
maxTokens: config.llm.maxTokens
|
|
44820
|
-
} : undefined
|
|
44821
|
-
});
|
|
44822
|
-
const identityManager = manager.getIdentityManager(assistant.id);
|
|
44823
|
-
await identityManager.initialize();
|
|
44824
|
-
await identityManager.createIdentity({ name: "Default" });
|
|
44825
|
-
if (await copyIfExists(join20(oldPath, "sessions"), join20(newPath, "assistants", assistant.id, "sessions"))) {
|
|
44826
|
-
result.migrated.push("sessions");
|
|
44827
|
-
}
|
|
44828
|
-
await writeFile8(join20(newPath, "migration", MIGRATION_MARKER), JSON.stringify({ migratedAt: new Date().toISOString() }, null, 2));
|
|
44829
|
-
const backupPath = `${oldPath}.backup`;
|
|
44830
|
-
await rename(oldPath, backupPath);
|
|
44831
|
-
result.backupPath = backupPath;
|
|
44832
|
-
result.migrated.push("backup");
|
|
44833
|
-
result.success = true;
|
|
44834
|
-
} catch (error) {
|
|
44835
|
-
result.errors.push(`Migration failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
44836
|
-
}
|
|
44837
|
-
return result;
|
|
44838
|
-
}
|
|
44839
45531
|
// packages/core/src/index.ts
|
|
44840
45532
|
init_anthropic();
|
|
44841
45533
|
// packages/core/src/client.ts
|
|
44842
45534
|
init_src();
|
|
44843
45535
|
|
|
44844
45536
|
// packages/core/src/logger.ts
|
|
44845
|
-
import { existsSync as
|
|
45537
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync8, appendFileSync, readdirSync as readdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
44846
45538
|
import { join as join21 } from "path";
|
|
44847
45539
|
class Logger {
|
|
44848
45540
|
logDir;
|
|
@@ -44856,8 +45548,8 @@ class Logger {
|
|
|
44856
45548
|
this.logFile = join21(this.logDir, `${date}.log`);
|
|
44857
45549
|
}
|
|
44858
45550
|
ensureDir(dir) {
|
|
44859
|
-
if (!
|
|
44860
|
-
|
|
45551
|
+
if (!existsSync12(dir)) {
|
|
45552
|
+
mkdirSync8(dir, { recursive: true });
|
|
44861
45553
|
}
|
|
44862
45554
|
}
|
|
44863
45555
|
write(level, message, data) {
|
|
@@ -44902,13 +45594,13 @@ class SessionStorage {
|
|
|
44902
45594
|
this.sessionFile = join21(this.sessionsDir, `${sessionId}.json`);
|
|
44903
45595
|
}
|
|
44904
45596
|
ensureDir(dir) {
|
|
44905
|
-
if (!
|
|
44906
|
-
|
|
45597
|
+
if (!existsSync12(dir)) {
|
|
45598
|
+
mkdirSync8(dir, { recursive: true });
|
|
44907
45599
|
}
|
|
44908
45600
|
}
|
|
44909
45601
|
save(data) {
|
|
44910
45602
|
try {
|
|
44911
|
-
|
|
45603
|
+
writeFileSync7(this.sessionFile, JSON.stringify(data, null, 2));
|
|
44912
45604
|
} catch {}
|
|
44913
45605
|
}
|
|
44914
45606
|
getSessionId() {
|
|
@@ -44916,9 +45608,9 @@ class SessionStorage {
|
|
|
44916
45608
|
}
|
|
44917
45609
|
load() {
|
|
44918
45610
|
try {
|
|
44919
|
-
if (!
|
|
45611
|
+
if (!existsSync12(this.sessionFile))
|
|
44920
45612
|
return null;
|
|
44921
|
-
return JSON.parse(
|
|
45613
|
+
return JSON.parse(readFileSync8(this.sessionFile, "utf-8"));
|
|
44922
45614
|
} catch {
|
|
44923
45615
|
return null;
|
|
44924
45616
|
}
|
|
@@ -44926,9 +45618,9 @@ class SessionStorage {
|
|
|
44926
45618
|
static getActiveAssistantId() {
|
|
44927
45619
|
try {
|
|
44928
45620
|
const activePath = join21(getConfigDir(), "active.json");
|
|
44929
|
-
if (!
|
|
45621
|
+
if (!existsSync12(activePath))
|
|
44930
45622
|
return null;
|
|
44931
|
-
const raw =
|
|
45623
|
+
const raw = readFileSync8(activePath, "utf-8");
|
|
44932
45624
|
const data = JSON.parse(raw);
|
|
44933
45625
|
return data.id || null;
|
|
44934
45626
|
} catch {
|
|
@@ -44940,7 +45632,7 @@ class SessionStorage {
|
|
|
44940
45632
|
const resolvedId = assistantId ?? SessionStorage.getActiveAssistantId();
|
|
44941
45633
|
if (resolvedId) {
|
|
44942
45634
|
const assistantDir = join21(root, "assistants", resolvedId, "sessions");
|
|
44943
|
-
if (
|
|
45635
|
+
if (existsSync12(assistantDir)) {
|
|
44944
45636
|
return assistantDir;
|
|
44945
45637
|
}
|
|
44946
45638
|
}
|
|
@@ -44948,17 +45640,17 @@ class SessionStorage {
|
|
|
44948
45640
|
}
|
|
44949
45641
|
static listSessions(assistantId) {
|
|
44950
45642
|
const sessionsDir = SessionStorage.resolveSessionsDir(assistantId);
|
|
44951
|
-
if (!
|
|
45643
|
+
if (!existsSync12(sessionsDir))
|
|
44952
45644
|
return [];
|
|
44953
45645
|
const sessions = [];
|
|
44954
|
-
const files =
|
|
45646
|
+
const files = readdirSync4(sessionsDir);
|
|
44955
45647
|
for (const file of files) {
|
|
44956
45648
|
if (!file.endsWith(".json"))
|
|
44957
45649
|
continue;
|
|
44958
45650
|
try {
|
|
44959
45651
|
const filePath = join21(sessionsDir, file);
|
|
44960
45652
|
const stat = Bun.file(filePath);
|
|
44961
|
-
const content = JSON.parse(
|
|
45653
|
+
const content = JSON.parse(readFileSync8(filePath, "utf-8"));
|
|
44962
45654
|
sessions.push({
|
|
44963
45655
|
id: file.replace(".json", ""),
|
|
44964
45656
|
cwd: content.cwd,
|
|
@@ -44978,9 +45670,9 @@ class SessionStorage {
|
|
|
44978
45670
|
const sessionsDir = SessionStorage.resolveSessionsDir(assistantId);
|
|
44979
45671
|
const sessionFile = join21(sessionsDir, `${sessionId}.json`);
|
|
44980
45672
|
try {
|
|
44981
|
-
if (!
|
|
45673
|
+
if (!existsSync12(sessionFile))
|
|
44982
45674
|
return null;
|
|
44983
|
-
return JSON.parse(
|
|
45675
|
+
return JSON.parse(readFileSync8(sessionFile, "utf-8"));
|
|
44984
45676
|
} catch {
|
|
44985
45677
|
return null;
|
|
44986
45678
|
}
|
|
@@ -45001,8 +45693,8 @@ function initAssistantsDir() {
|
|
|
45001
45693
|
join21(baseDir, "migration")
|
|
45002
45694
|
];
|
|
45003
45695
|
for (const dir of dirs) {
|
|
45004
|
-
if (!
|
|
45005
|
-
|
|
45696
|
+
if (!existsSync12(dir)) {
|
|
45697
|
+
mkdirSync8(dir, { recursive: true });
|
|
45006
45698
|
}
|
|
45007
45699
|
}
|
|
45008
45700
|
}
|
|
@@ -45022,6 +45714,7 @@ class EmbeddedClient {
|
|
|
45022
45714
|
assistantId = null;
|
|
45023
45715
|
messageQueue = [];
|
|
45024
45716
|
processingQueue = false;
|
|
45717
|
+
sawErrorChunk = false;
|
|
45025
45718
|
constructor(cwd2, options) {
|
|
45026
45719
|
initAssistantsDir();
|
|
45027
45720
|
const sessionId = options?.sessionId || generateId();
|
|
@@ -45041,6 +45734,9 @@ class EmbeddedClient {
|
|
|
45041
45734
|
for (const callback of this.chunkCallbacks) {
|
|
45042
45735
|
callback(chunk);
|
|
45043
45736
|
}
|
|
45737
|
+
if (chunk.type === "error") {
|
|
45738
|
+
this.sawErrorChunk = true;
|
|
45739
|
+
}
|
|
45044
45740
|
if (chunk.type === "done" || chunk.type === "error") {
|
|
45045
45741
|
queueMicrotask(() => {
|
|
45046
45742
|
this.drainQueue();
|
|
@@ -45097,6 +45793,7 @@ class EmbeddedClient {
|
|
|
45097
45793
|
}
|
|
45098
45794
|
async processMessage(message) {
|
|
45099
45795
|
this.logger.info("User message", { message });
|
|
45796
|
+
this.sawErrorChunk = false;
|
|
45100
45797
|
try {
|
|
45101
45798
|
await this.agent.process(message);
|
|
45102
45799
|
const context = this.agent.getContext();
|
|
@@ -45113,6 +45810,9 @@ class EmbeddedClient {
|
|
|
45113
45810
|
}
|
|
45114
45811
|
this.saveSession();
|
|
45115
45812
|
} catch (error) {
|
|
45813
|
+
if (this.sawErrorChunk) {
|
|
45814
|
+
return;
|
|
45815
|
+
}
|
|
45116
45816
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
45117
45817
|
this.logger.error("Error processing message", { error: err.message });
|
|
45118
45818
|
for (const callback of this.errorCallbacks) {
|
|
@@ -45267,6 +45967,9 @@ class SessionRegistry {
|
|
|
45267
45967
|
const session = this.sessions.get(sessionId);
|
|
45268
45968
|
if (session) {
|
|
45269
45969
|
session.updatedAt = Date.now();
|
|
45970
|
+
if (chunk.type === "text" || chunk.type === "tool_use" || chunk.type === "tool_result") {
|
|
45971
|
+
session.isProcessing = true;
|
|
45972
|
+
}
|
|
45270
45973
|
if (chunk.type === "done" || chunk.type === "error" || chunk.type === "exit") {
|
|
45271
45974
|
session.isProcessing = false;
|
|
45272
45975
|
}
|
|
@@ -45374,7 +46077,6 @@ init_errors();
|
|
|
45374
46077
|
init_src();
|
|
45375
46078
|
|
|
45376
46079
|
// packages/terminal/src/components/App.tsx
|
|
45377
|
-
var import_react29 = __toESM(require_react(), 1);
|
|
45378
46080
|
init_src();
|
|
45379
46081
|
|
|
45380
46082
|
// packages/terminal/src/components/Input.tsx
|
|
@@ -46449,9 +47151,9 @@ function estimateToolPanelLines(toolCalls, toolResults, hasContent) {
|
|
|
46449
47151
|
if (!toolCalls || toolCalls.length === 0) {
|
|
46450
47152
|
return 0;
|
|
46451
47153
|
}
|
|
46452
|
-
const
|
|
47154
|
+
const resultLines = new Map;
|
|
46453
47155
|
for (const result of toolResults || []) {
|
|
46454
|
-
|
|
47156
|
+
resultLines.set(result.toolCallId, estimateToolResultLines(result));
|
|
46455
47157
|
}
|
|
46456
47158
|
let lines = 3;
|
|
46457
47159
|
if (hasContent) {
|
|
@@ -46459,26 +47161,53 @@ function estimateToolPanelLines(toolCalls, toolResults, hasContent) {
|
|
|
46459
47161
|
}
|
|
46460
47162
|
for (const call of toolCalls) {
|
|
46461
47163
|
lines += 3;
|
|
46462
|
-
|
|
46463
|
-
|
|
47164
|
+
const toolResultLines = resultLines.get(call.id);
|
|
47165
|
+
if (toolResultLines && toolResultLines > 0) {
|
|
47166
|
+
lines += toolResultLines;
|
|
46464
47167
|
}
|
|
46465
47168
|
}
|
|
46466
47169
|
return lines;
|
|
46467
47170
|
}
|
|
46468
|
-
function
|
|
47171
|
+
function estimateToolResultLines(result, maxLines = 4) {
|
|
47172
|
+
const content = String(result.content || "");
|
|
47173
|
+
if (!content)
|
|
47174
|
+
return 1;
|
|
47175
|
+
const lines = content.replace(/\x1B\[[0-9;]*m/g, "").split(`
|
|
47176
|
+
`);
|
|
47177
|
+
if (lines.length <= maxLines)
|
|
47178
|
+
return Math.max(1, lines.length);
|
|
47179
|
+
return maxLines + 1;
|
|
47180
|
+
}
|
|
47181
|
+
function estimateMessageLines(message, maxWidth) {
|
|
46469
47182
|
if (message.role === "system") {
|
|
46470
47183
|
return 0;
|
|
46471
47184
|
}
|
|
46472
47185
|
const content = message.content ?? "";
|
|
46473
47186
|
const contentLines = content.length > 0 ? content.split(`
|
|
46474
|
-
`)
|
|
46475
|
-
const hasContent = contentLines > 0;
|
|
46476
|
-
|
|
47187
|
+
`) : [];
|
|
47188
|
+
const hasContent = contentLines.length > 0;
|
|
47189
|
+
const wrappedLines = contentLines.length > 0 ? countWrappedLines(contentLines, maxWidth) : 0;
|
|
47190
|
+
let lines = hasContent ? Math.max(1, wrappedLines) : 0;
|
|
46477
47191
|
if (message.role === "assistant" && message.toolCalls?.length) {
|
|
46478
47192
|
lines += estimateToolPanelLines(message.toolCalls, message.toolResults, hasContent);
|
|
46479
47193
|
}
|
|
46480
47194
|
return lines;
|
|
46481
47195
|
}
|
|
47196
|
+
function countWrappedLines(lines, maxWidth) {
|
|
47197
|
+
if (!maxWidth || maxWidth <= 0) {
|
|
47198
|
+
return lines.length;
|
|
47199
|
+
}
|
|
47200
|
+
let total = 0;
|
|
47201
|
+
for (const line of lines) {
|
|
47202
|
+
const visible = stripAnsi3(line).length;
|
|
47203
|
+
const wrapped = Math.max(1, Math.ceil(visible / maxWidth));
|
|
47204
|
+
total += wrapped;
|
|
47205
|
+
}
|
|
47206
|
+
return total;
|
|
47207
|
+
}
|
|
47208
|
+
function stripAnsi3(text) {
|
|
47209
|
+
return text.replace(/\x1B\[[0-9;]*m/g, "");
|
|
47210
|
+
}
|
|
46482
47211
|
|
|
46483
47212
|
// packages/terminal/src/components/Messages.tsx
|
|
46484
47213
|
var jsx_dev_runtime3 = __toESM(require_jsx_dev_runtime(), 1);
|
|
@@ -46494,16 +47223,18 @@ function Messages4({
|
|
|
46494
47223
|
queuedMessageIds
|
|
46495
47224
|
}) {
|
|
46496
47225
|
const [now2, setNow] = import_react24.useState(Date.now());
|
|
47226
|
+
const { columns } = use_stdout_default();
|
|
47227
|
+
const messageWidth = columns ? Math.max(10, columns - 2) : undefined;
|
|
46497
47228
|
const combinedMessages = import_react24.useMemo(() => [...messages, ...streamingMessages], [messages, streamingMessages]);
|
|
46498
47229
|
const lineSpans = import_react24.useMemo(() => {
|
|
46499
47230
|
let cursor = 0;
|
|
46500
47231
|
return combinedMessages.map((message, index) => {
|
|
46501
|
-
const lines = estimateMessageLines(message);
|
|
47232
|
+
const lines = estimateMessageLines(message, messageWidth);
|
|
46502
47233
|
const start = cursor;
|
|
46503
47234
|
cursor += lines;
|
|
46504
47235
|
return { message, index, start, end: cursor, lines };
|
|
46505
47236
|
});
|
|
46506
|
-
}, [combinedMessages]);
|
|
47237
|
+
}, [combinedMessages, messageWidth]);
|
|
46507
47238
|
const totalLines = lineSpans.length > 0 ? lineSpans[lineSpans.length - 1].end : 0;
|
|
46508
47239
|
const endLine = Math.max(0, totalLines - scrollOffsetLines);
|
|
46509
47240
|
const startLine = Math.max(0, endLine - maxVisibleLines);
|
|
@@ -46511,6 +47242,7 @@ function Messages4({
|
|
|
46511
47242
|
const historicalCount = messages.length;
|
|
46512
47243
|
const visibleMessages = visibleSpans.filter((span) => span.index < historicalCount).map((span) => span.message);
|
|
46513
47244
|
const visibleStreaming = visibleSpans.filter((span) => span.index >= historicalCount).map((span) => span.message);
|
|
47245
|
+
const showCurrentResponse = Boolean(currentResponse) && streamingMessages.length === 0;
|
|
46514
47246
|
const groupedMessages = groupConsecutiveToolMessages(visibleMessages);
|
|
46515
47247
|
const historicalItems = groupedMessages.map((group) => {
|
|
46516
47248
|
if (group.type === "single") {
|
|
@@ -46636,7 +47368,7 @@ function Messages4({
|
|
|
46636
47368
|
message,
|
|
46637
47369
|
queuedMessageIds
|
|
46638
47370
|
}, message.id, false, undefined, this)),
|
|
46639
|
-
|
|
47371
|
+
showCurrentResponse && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
46640
47372
|
marginY: 1,
|
|
46641
47373
|
children: [
|
|
46642
47374
|
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
@@ -47597,7 +48329,7 @@ function SessionSelector({
|
|
|
47597
48329
|
|
|
47598
48330
|
// packages/terminal/src/components/App.tsx
|
|
47599
48331
|
var jsx_dev_runtime9 = __toESM(require_jsx_dev_runtime(), 1);
|
|
47600
|
-
var SHOW_ERROR_CODES = process.env.ASSISTANTS_DEBUG === "1"
|
|
48332
|
+
var SHOW_ERROR_CODES = process.env.ASSISTANTS_DEBUG === "1";
|
|
47601
48333
|
function parseErrorMessage(error) {
|
|
47602
48334
|
const lines = error.split(`
|
|
47603
48335
|
`);
|
|
@@ -47642,15 +48374,30 @@ function wrapTextLines(text, wrapChars) {
|
|
|
47642
48374
|
}
|
|
47643
48375
|
return lines;
|
|
47644
48376
|
}
|
|
47645
|
-
function
|
|
48377
|
+
function stripAnsi4(text) {
|
|
47646
48378
|
return text.replace(/\x1B\[[0-9;]*m/g, "");
|
|
47647
48379
|
}
|
|
48380
|
+
function truncateActivityContent(content, maxLines = 15, maxChars = 3000) {
|
|
48381
|
+
let output = content.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
|
|
48382
|
+
output = output.replace(/\t/g, " ");
|
|
48383
|
+
const lines = output.split(`
|
|
48384
|
+
`);
|
|
48385
|
+
if (lines.length > maxLines) {
|
|
48386
|
+
output = lines.slice(0, maxLines).join(`
|
|
48387
|
+
`) + `
|
|
48388
|
+
... (${lines.length - maxLines} more lines)`;
|
|
48389
|
+
}
|
|
48390
|
+
if (output.length > maxChars) {
|
|
48391
|
+
output = output.slice(0, maxChars) + "...";
|
|
48392
|
+
}
|
|
48393
|
+
return output;
|
|
48394
|
+
}
|
|
47648
48395
|
function chunkRenderedLines(lines, chunkLines) {
|
|
47649
48396
|
const chunks = [];
|
|
47650
48397
|
let current = [];
|
|
47651
48398
|
let i = 0;
|
|
47652
|
-
const isBoxStart = (line) =>
|
|
47653
|
-
const isBoxEnd = (line) =>
|
|
48399
|
+
const isBoxStart = (line) => stripAnsi4(line).trimStart().startsWith("\u250C");
|
|
48400
|
+
const isBoxEnd = (line) => stripAnsi4(line).trimStart().startsWith("\u2514");
|
|
47654
48401
|
while (i < lines.length) {
|
|
47655
48402
|
const line = lines[i];
|
|
47656
48403
|
if (isBoxStart(line)) {
|
|
@@ -47760,9 +48507,14 @@ function App2({ cwd: cwd2, version }) {
|
|
|
47760
48507
|
const prevDisplayLineCountRef = import_react29.useRef(0);
|
|
47761
48508
|
const skipNextDoneRef = import_react29.useRef(false);
|
|
47762
48509
|
const isProcessingRef = import_react29.useRef(isProcessing);
|
|
48510
|
+
const processingStartTimeRef = import_react29.useRef(processingStartTime);
|
|
48511
|
+
const turnIdRef = import_react29.useRef(0);
|
|
47763
48512
|
import_react29.useEffect(() => {
|
|
47764
48513
|
isProcessingRef.current = isProcessing;
|
|
47765
48514
|
}, [isProcessing]);
|
|
48515
|
+
import_react29.useEffect(() => {
|
|
48516
|
+
processingStartTimeRef.current = processingStartTime;
|
|
48517
|
+
}, [processingStartTime]);
|
|
47766
48518
|
import_react29.useEffect(() => {
|
|
47767
48519
|
if (isProcessing && !processingStartTime) {
|
|
47768
48520
|
setProcessingStartTime(Date.now());
|
|
@@ -47815,8 +48567,8 @@ function App2({ cwd: cwd2, version }) {
|
|
|
47815
48567
|
|
|
47816
48568
|
[error]` : "[error]";
|
|
47817
48569
|
}
|
|
47818
|
-
if (
|
|
47819
|
-
const workedFor = formatElapsedDuration(Date.now() -
|
|
48570
|
+
if (processingStartTimeRef.current) {
|
|
48571
|
+
const workedFor = formatElapsedDuration(Date.now() - processingStartTimeRef.current);
|
|
47820
48572
|
content = content ? `${content}
|
|
47821
48573
|
|
|
47822
48574
|
\u273B Worked for ${workedFor}` : `\u273B Worked for ${workedFor}`;
|
|
@@ -47833,7 +48585,7 @@ function App2({ cwd: cwd2, version }) {
|
|
|
47833
48585
|
}
|
|
47834
48586
|
]);
|
|
47835
48587
|
return true;
|
|
47836
|
-
}, [buildFullResponse
|
|
48588
|
+
}, [buildFullResponse]);
|
|
47837
48589
|
const resetTurnState = import_react29.useCallback(() => {
|
|
47838
48590
|
setCurrentResponse("");
|
|
47839
48591
|
responseRef.current = "";
|
|
@@ -47902,9 +48654,14 @@ function App2({ cwd: cwd2, version }) {
|
|
|
47902
48654
|
setAutoScroll(true);
|
|
47903
48655
|
}, []);
|
|
47904
48656
|
const handleChunk = import_react29.useCallback((chunk) => {
|
|
47905
|
-
|
|
48657
|
+
const isStartChunk = chunk.type === "text" || chunk.type === "tool_use";
|
|
48658
|
+
const isTerminalChunk = chunk.type === "error" || chunk.type === "done";
|
|
48659
|
+
if (!isProcessingRef.current && (isStartChunk || isTerminalChunk)) {
|
|
47906
48660
|
const active = registryRef.current.getActiveSession();
|
|
47907
48661
|
if (active) {
|
|
48662
|
+
turnIdRef.current += 1;
|
|
48663
|
+
resetTurnState();
|
|
48664
|
+
setError(null);
|
|
47908
48665
|
registryRef.current.setProcessing(active.id, true);
|
|
47909
48666
|
setIsProcessing(true);
|
|
47910
48667
|
isProcessingRef.current = true;
|
|
@@ -48004,8 +48761,11 @@ function App2({ cwd: cwd2, version }) {
|
|
|
48004
48761
|
if (active) {
|
|
48005
48762
|
registryRef.current.setProcessing(active.id, false);
|
|
48006
48763
|
}
|
|
48764
|
+
const turnId = turnIdRef.current;
|
|
48007
48765
|
queueMicrotask(() => {
|
|
48008
|
-
|
|
48766
|
+
if (!isProcessingRef.current && turnIdRef.current === turnId) {
|
|
48767
|
+
resetTurnState();
|
|
48768
|
+
}
|
|
48009
48769
|
});
|
|
48010
48770
|
const activeSession2 = registry2.getActiveSession();
|
|
48011
48771
|
if (activeSession2) {
|
|
@@ -48102,8 +48862,94 @@ function App2({ cwd: cwd2, version }) {
|
|
|
48102
48862
|
const activeQueue = activeSessionId ? messageQueue.filter((msg) => msg.sessionId === activeSessionId) : [];
|
|
48103
48863
|
const activeInline = activeSessionId ? inlinePending.filter((msg) => msg.sessionId === activeSessionId) : [];
|
|
48104
48864
|
const queuedMessageIds = import_react29.useMemo(() => new Set(activeQueue.filter((msg) => msg.mode === "queued").map((msg) => msg.id)), [activeQueue]);
|
|
48865
|
+
const sessions = registry2.listSessions();
|
|
48866
|
+
const activeSession = registry2.getActiveSession();
|
|
48867
|
+
const sessionIndex = activeSessionId ? registry2.getSessionIndex(activeSessionId) : 0;
|
|
48868
|
+
const sessionCount = registry2.getSessionCount();
|
|
48869
|
+
const backgroundProcessingCount = registry2.getBackgroundProcessingSessions().length;
|
|
48870
|
+
const MAX_QUEUED_PREVIEW = 3;
|
|
48871
|
+
const truncateQueued = (text, maxLen = 80) => {
|
|
48872
|
+
if (text.length <= maxLen)
|
|
48873
|
+
return text;
|
|
48874
|
+
return text.slice(0, maxLen - 3) + "...";
|
|
48875
|
+
};
|
|
48876
|
+
const queuedCount = activeQueue.filter((msg) => msg.mode === "queued").length;
|
|
48877
|
+
const inlineCount = activeInline.length;
|
|
48878
|
+
const showWelcome = messages.length === 0 && !isProcessing;
|
|
48105
48879
|
const wrapChars = columns ? Math.max(40, columns - 4) : MESSAGE_WRAP_CHARS;
|
|
48106
48880
|
const renderWidth = columns ? Math.max(20, columns - 2) : undefined;
|
|
48881
|
+
const activityLogLineCount = import_react29.useMemo(() => {
|
|
48882
|
+
if (activityLog.length === 0)
|
|
48883
|
+
return 0;
|
|
48884
|
+
let lines = 0;
|
|
48885
|
+
for (const entry of activityLog) {
|
|
48886
|
+
if (entry.type === "text" && entry.content) {
|
|
48887
|
+
const rendered = renderMarkdown(entry.content, { maxWidth: renderWidth });
|
|
48888
|
+
const entryLines = wrapTextLines(stripAnsi4(rendered), wrapChars).length;
|
|
48889
|
+
lines += entryLines + 2;
|
|
48890
|
+
continue;
|
|
48891
|
+
}
|
|
48892
|
+
if (entry.type === "tool_call" && entry.toolCall) {
|
|
48893
|
+
lines += 4;
|
|
48894
|
+
continue;
|
|
48895
|
+
}
|
|
48896
|
+
if (entry.type === "tool_result" && entry.toolResult) {
|
|
48897
|
+
const truncated = truncateActivityContent(String(entry.toolResult.content ?? ""));
|
|
48898
|
+
const entryLines = wrapTextLines(truncated, wrapChars).length;
|
|
48899
|
+
lines += entryLines + 2;
|
|
48900
|
+
continue;
|
|
48901
|
+
}
|
|
48902
|
+
}
|
|
48903
|
+
return lines;
|
|
48904
|
+
}, [activityLog, renderWidth, wrapChars]);
|
|
48905
|
+
const reservedLines = import_react29.useMemo(() => {
|
|
48906
|
+
let lines = 0;
|
|
48907
|
+
if (showWelcome) {
|
|
48908
|
+
lines += 5;
|
|
48909
|
+
}
|
|
48910
|
+
if (backgroundProcessingCount > 0) {
|
|
48911
|
+
lines += 1;
|
|
48912
|
+
}
|
|
48913
|
+
if (scrollOffset > 0) {
|
|
48914
|
+
lines += 1;
|
|
48915
|
+
}
|
|
48916
|
+
if (activeQueue.length > 0 || inlineCount > 0) {
|
|
48917
|
+
const previewCount = Math.min(MAX_QUEUED_PREVIEW, activeQueue.length + inlineCount);
|
|
48918
|
+
const hasMore = activeQueue.length + inlineCount > MAX_QUEUED_PREVIEW;
|
|
48919
|
+
lines += 2;
|
|
48920
|
+
lines += 1;
|
|
48921
|
+
lines += previewCount;
|
|
48922
|
+
if (hasMore)
|
|
48923
|
+
lines += 1;
|
|
48924
|
+
}
|
|
48925
|
+
if (error) {
|
|
48926
|
+
const parsed = parseErrorMessage(error);
|
|
48927
|
+
const messageLines = parsed.message ? parsed.message.split(`
|
|
48928
|
+
`).length : 1;
|
|
48929
|
+
const suggestionLines = parsed.suggestion ? parsed.suggestion.split(`
|
|
48930
|
+
`).length : 0;
|
|
48931
|
+
lines += 2;
|
|
48932
|
+
lines += Math.max(1, messageLines) + suggestionLines;
|
|
48933
|
+
}
|
|
48934
|
+
if (isProcessing) {
|
|
48935
|
+
lines += 3;
|
|
48936
|
+
}
|
|
48937
|
+
lines += 1;
|
|
48938
|
+
lines += 1;
|
|
48939
|
+
if (isProcessing) {
|
|
48940
|
+
lines += 2;
|
|
48941
|
+
}
|
|
48942
|
+
lines += 2;
|
|
48943
|
+
return lines;
|
|
48944
|
+
}, [
|
|
48945
|
+
activeQueue.length,
|
|
48946
|
+
inlineCount,
|
|
48947
|
+
backgroundProcessingCount,
|
|
48948
|
+
scrollOffset,
|
|
48949
|
+
error,
|
|
48950
|
+
isProcessing,
|
|
48951
|
+
showWelcome
|
|
48952
|
+
]);
|
|
48107
48953
|
const displayMessages = import_react29.useMemo(() => buildDisplayMessages(messages, MESSAGE_CHUNK_LINES, wrapChars, { maxWidth: renderWidth }), [messages, wrapChars, renderWidth]);
|
|
48108
48954
|
const streamingMessages = import_react29.useMemo(() => {
|
|
48109
48955
|
if (!isProcessing || !currentResponse.trim())
|
|
@@ -48118,8 +48964,9 @@ function App2({ cwd: cwd2, version }) {
|
|
|
48118
48964
|
}, [currentResponse, isProcessing, wrapChars, renderWidth]);
|
|
48119
48965
|
const displayLineCount = import_react29.useMemo(() => {
|
|
48120
48966
|
const combined = [...displayMessages, ...streamingMessages];
|
|
48121
|
-
return combined.reduce((sum, msg) => sum + estimateMessageLines(msg), 0);
|
|
48122
|
-
}, [displayMessages, streamingMessages]);
|
|
48967
|
+
return combined.reduce((sum, msg) => sum + estimateMessageLines(msg, renderWidth), 0);
|
|
48968
|
+
}, [displayMessages, streamingMessages, renderWidth]);
|
|
48969
|
+
const totalLineCount = displayLineCount + activityLogLineCount;
|
|
48123
48970
|
import_react29.useEffect(() => {
|
|
48124
48971
|
if (!isProcessing && activeQueue.length > 0) {
|
|
48125
48972
|
processQueue();
|
|
@@ -48129,26 +48976,20 @@ function App2({ cwd: cwd2, version }) {
|
|
|
48129
48976
|
if (autoScroll) {
|
|
48130
48977
|
setScrollOffset(0);
|
|
48131
48978
|
}
|
|
48132
|
-
}, [
|
|
48979
|
+
}, [totalLineCount, autoScroll]);
|
|
48133
48980
|
import_react29.useEffect(() => {
|
|
48134
48981
|
const prevCount = prevDisplayLineCountRef.current;
|
|
48135
|
-
if (!autoScroll &&
|
|
48136
|
-
const delta =
|
|
48982
|
+
if (!autoScroll && totalLineCount > prevCount) {
|
|
48983
|
+
const delta = totalLineCount - prevCount;
|
|
48137
48984
|
setScrollOffset((prev) => prev + delta);
|
|
48138
48985
|
}
|
|
48139
|
-
prevDisplayLineCountRef.current =
|
|
48140
|
-
}, [
|
|
48141
|
-
const
|
|
48142
|
-
const maxVisibleLines = rows ? Math.max(6, rows - reservedLines) : 20;
|
|
48986
|
+
prevDisplayLineCountRef.current = totalLineCount;
|
|
48987
|
+
}, [totalLineCount, autoScroll]);
|
|
48988
|
+
const maxVisibleLines = rows ? Math.max(4, rows - reservedLines) : 20;
|
|
48143
48989
|
import_react29.useEffect(() => {
|
|
48144
|
-
const maxOffset = Math.max(0,
|
|
48990
|
+
const maxOffset = Math.max(0, totalLineCount - maxVisibleLines);
|
|
48145
48991
|
setScrollOffset((prev) => Math.min(prev, maxOffset));
|
|
48146
|
-
}, [
|
|
48147
|
-
const sessions = registry2.listSessions();
|
|
48148
|
-
const activeSession = registry2.getActiveSession();
|
|
48149
|
-
const sessionIndex = activeSessionId ? registry2.getSessionIndex(activeSessionId) : 0;
|
|
48150
|
-
const sessionCount = registry2.getSessionCount();
|
|
48151
|
-
const backgroundProcessingCount = registry2.getBackgroundProcessingSessions().length;
|
|
48992
|
+
}, [totalLineCount, maxVisibleLines]);
|
|
48152
48993
|
const handleSessionSwitch = import_react29.useCallback(async (sessionId) => {
|
|
48153
48994
|
setShowSessionSelector(false);
|
|
48154
48995
|
if (sessionId === activeSessionId) {
|
|
@@ -48222,7 +49063,7 @@ function App2({ cwd: cwd2, version }) {
|
|
|
48222
49063
|
}
|
|
48223
49064
|
if (key.pageUp || key.shift && key.upArrow) {
|
|
48224
49065
|
setScrollOffset((prev) => {
|
|
48225
|
-
const maxOffset = Math.max(0,
|
|
49066
|
+
const maxOffset = Math.max(0, totalLineCount - maxVisibleLines);
|
|
48226
49067
|
const step = Math.max(3, Math.floor(maxVisibleLines / 2));
|
|
48227
49068
|
const newOffset = Math.min(prev + step, maxOffset);
|
|
48228
49069
|
if (newOffset > 0)
|
|
@@ -48240,7 +49081,7 @@ function App2({ cwd: cwd2, version }) {
|
|
|
48240
49081
|
});
|
|
48241
49082
|
}
|
|
48242
49083
|
if (key.ctrl && input === "u") {
|
|
48243
|
-
const maxOffset = Math.max(0,
|
|
49084
|
+
const maxOffset = Math.max(0, totalLineCount - maxVisibleLines);
|
|
48244
49085
|
setScrollOffset(maxOffset);
|
|
48245
49086
|
setAutoScroll(false);
|
|
48246
49087
|
}
|
|
@@ -48399,15 +49240,6 @@ function App2({ cwd: cwd2, version }) {
|
|
|
48399
49240
|
return { toolCall: e.toolCall, result };
|
|
48400
49241
|
});
|
|
48401
49242
|
const isThinking = isProcessing && !currentResponse && !currentToolCall && toolCallEntries.length === 0;
|
|
48402
|
-
const MAX_QUEUED_PREVIEW = 3;
|
|
48403
|
-
const truncateQueued = (text, maxLen = 80) => {
|
|
48404
|
-
if (text.length <= maxLen)
|
|
48405
|
-
return text;
|
|
48406
|
-
return text.slice(0, maxLen - 3) + "...";
|
|
48407
|
-
};
|
|
48408
|
-
const queuedCount = activeQueue.filter((msg) => msg.mode === "queued").length;
|
|
48409
|
-
const inlineCount = activeInline.length;
|
|
48410
|
-
const showWelcome = messages.length === 0 && !isProcessing;
|
|
48411
49243
|
return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Box_default, {
|
|
48412
49244
|
flexDirection: "column",
|
|
48413
49245
|
padding: 1,
|
|
@@ -48714,7 +49546,7 @@ function formatStreamEvent(chunk) {
|
|
|
48714
49546
|
|
|
48715
49547
|
// packages/terminal/src/index.tsx
|
|
48716
49548
|
var jsx_dev_runtime10 = __toESM(require_jsx_dev_runtime(), 1);
|
|
48717
|
-
var VERSION3 = "0.6.
|
|
49549
|
+
var VERSION3 = "0.6.22";
|
|
48718
49550
|
process.env.ASSISTANTS_VERSION ??= VERSION3;
|
|
48719
49551
|
function parseArgs(argv) {
|
|
48720
49552
|
const args = argv.slice(2);
|
|
@@ -48841,7 +49673,6 @@ Interactive Mode:
|
|
|
48841
49673
|
`);
|
|
48842
49674
|
process.exit(0);
|
|
48843
49675
|
}
|
|
48844
|
-
migrateFromOldpal().catch(() => {});
|
|
48845
49676
|
if (options.print !== null) {
|
|
48846
49677
|
if (!options.print.trim()) {
|
|
48847
49678
|
console.error("Error: Prompt is required with -p/--print flag");
|
|
@@ -48871,4 +49702,4 @@ if (options.print !== null) {
|
|
|
48871
49702
|
});
|
|
48872
49703
|
}
|
|
48873
49704
|
|
|
48874
|
-
//# debugId=
|
|
49705
|
+
//# debugId=A428BC3DC0A7465964756E2164756E21
|