wave-agent-sdk 0.14.1 → 0.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/builtin/skills/settings/HOOKS.md +69 -0
- package/builtin/skills/settings/PLUGINS.md +171 -0
- package/builtin/skills/settings/SKILL.md +8 -3
- package/dist/agent.d.ts +2 -2
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +12 -3
- package/dist/managers/aiManager.d.ts +6 -6
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +122 -59
- package/dist/managers/backgroundTaskManager.d.ts.map +1 -1
- package/dist/managers/backgroundTaskManager.js +28 -18
- package/dist/managers/hookManager.d.ts +16 -1
- package/dist/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +97 -8
- package/dist/managers/messageManager.d.ts +19 -4
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +63 -18
- package/dist/prompts/index.d.ts +1 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +1 -1
- package/dist/services/MarketplaceService.d.ts +0 -11
- package/dist/services/MarketplaceService.d.ts.map +1 -1
- package/dist/services/MarketplaceService.js +21 -89
- package/dist/services/aiService.d.ts +3 -3
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +7 -7
- package/dist/services/hook.d.ts.map +1 -1
- package/dist/services/hook.js +15 -0
- package/dist/services/initializationService.d.ts.map +1 -1
- package/dist/services/initializationService.js +24 -1
- package/dist/services/interactionService.js +1 -1
- package/dist/services/pluginLoader.d.ts +0 -6
- package/dist/services/pluginLoader.d.ts.map +1 -1
- package/dist/services/pluginLoader.js +14 -53
- package/dist/services/session.d.ts +1 -1
- package/dist/services/session.js +7 -7
- package/dist/services/taskManager.d.ts +1 -1
- package/dist/services/taskManager.js +1 -1
- package/dist/types/core.d.ts +1 -1
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/hooks.d.ts +9 -1
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/hooks.js +2 -0
- package/dist/types/marketplace.d.ts +1 -26
- package/dist/types/marketplace.d.ts.map +1 -1
- package/dist/types/messaging.d.ts +3 -3
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/types/plugins.d.ts +1 -13
- package/dist/types/plugins.d.ts.map +1 -1
- package/dist/utils/convertMessagesForAPI.d.ts +1 -1
- package/dist/utils/convertMessagesForAPI.js +7 -7
- package/dist/utils/groupMessagesByApiRound.d.ts +1 -1
- package/dist/utils/groupMessagesByApiRound.js +6 -6
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +3 -3
- package/package.json +1 -1
- package/src/agent.ts +16 -3
- package/src/managers/aiManager.ts +142 -63
- package/src/managers/backgroundTaskManager.ts +32 -22
- package/src/managers/hookManager.ts +125 -10
- package/src/managers/messageManager.ts +76 -22
- package/src/prompts/index.ts +1 -1
- package/src/services/MarketplaceService.ts +26 -127
- package/src/services/aiService.ts +11 -11
- package/src/services/hook.ts +17 -0
- package/src/services/initializationService.ts +33 -1
- package/src/services/interactionService.ts +1 -1
- package/src/services/pluginLoader.ts +14 -67
- package/src/services/session.ts +7 -7
- package/src/services/taskManager.ts +1 -1
- package/src/types/core.ts +1 -1
- package/src/types/hooks.ts +16 -2
- package/src/types/marketplace.ts +1 -24
- package/src/types/messaging.ts +3 -3
- package/src/types/plugins.ts +1 -13
- package/src/utils/convertMessagesForAPI.ts +8 -8
- package/src/utils/groupMessagesByApiRound.ts +6 -6
- package/src/utils/messageOperations.ts +3 -5
|
@@ -47,8 +47,8 @@ export interface MessageManagerCallbacks {
|
|
|
47
47
|
onAssistantReasoningUpdated?: (chunk: string, accumulated: string) => void;
|
|
48
48
|
onToolBlockUpdated?: (params: AgentToolBlockUpdateParams) => void;
|
|
49
49
|
onErrorBlockAdded?: (error: string) => void;
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
onCompactBlockAdded?: (content: string) => void;
|
|
51
|
+
onCompactionStateChange?: (isCompacting: boolean) => void;
|
|
52
52
|
// Bang callback
|
|
53
53
|
onAddBangMessage?: (command: string) => void;
|
|
54
54
|
onUpdateBangMessage?: (command: string, output: string) => void;
|
|
@@ -92,6 +92,8 @@ export class MessageManager {
|
|
|
92
92
|
private filesInContext: Set<string> = new Set(); // Track files mentioned in the conversation
|
|
93
93
|
private recentFileReads: Map<string, { content: string; timestamp: number }> =
|
|
94
94
|
new Map(); // Track file read contents
|
|
95
|
+
private invokedSkills: Map<string, { skillName: string; timestamp: number }> =
|
|
96
|
+
new Map(); // Track invoked skill names
|
|
95
97
|
private sessionType: "main" | "subagent";
|
|
96
98
|
private subagentType?: string;
|
|
97
99
|
private _usages: Usage[] = [];
|
|
@@ -270,12 +272,14 @@ export class MessageManager {
|
|
|
270
272
|
for (const message of newMessages) {
|
|
271
273
|
this.addPathsFromMessage(message);
|
|
272
274
|
this.extractFileReadsFromMessage(message);
|
|
275
|
+
this.extractSkillInvocationsFromMessage(message);
|
|
273
276
|
}
|
|
274
277
|
|
|
275
278
|
// Also check if the last message was updated (common for tool blocks)
|
|
276
279
|
if (messages.length > 0 && messages.length === oldLength) {
|
|
277
280
|
this.addPathsFromMessage(messages[messages.length - 1]);
|
|
278
281
|
this.extractFileReadsFromMessage(messages[messages.length - 1]);
|
|
282
|
+
this.extractSkillInvocationsFromMessage(messages[messages.length - 1]);
|
|
279
283
|
}
|
|
280
284
|
|
|
281
285
|
this.callbacks.onMessagesChange?.([...messages]);
|
|
@@ -494,31 +498,31 @@ export class MessageManager {
|
|
|
494
498
|
}
|
|
495
499
|
|
|
496
500
|
/**
|
|
497
|
-
*
|
|
501
|
+
* Compact messages and update session, delete compacted messages, only keep compacted messages and last 3 messages
|
|
498
502
|
*/
|
|
499
|
-
public
|
|
500
|
-
|
|
503
|
+
public compactMessagesAndUpdateSession(
|
|
504
|
+
compactedContent: string,
|
|
501
505
|
usage?: Usage,
|
|
502
506
|
): void {
|
|
503
507
|
// Get last 2 API rounds to preserve (structurally safe boundary)
|
|
504
508
|
const lastThreeMessages = getLastApiRounds(this.messages, 2);
|
|
505
509
|
|
|
506
|
-
// Create
|
|
507
|
-
const
|
|
510
|
+
// Create compacted message
|
|
511
|
+
const compactMessage: Message = {
|
|
508
512
|
id: generateMessageId(),
|
|
509
513
|
role: "assistant",
|
|
510
514
|
blocks: [
|
|
511
515
|
{
|
|
512
|
-
type: "
|
|
513
|
-
content:
|
|
516
|
+
type: "compact",
|
|
517
|
+
content: compactedContent,
|
|
514
518
|
sessionId: this.sessionId,
|
|
515
519
|
},
|
|
516
520
|
],
|
|
517
521
|
...(usage && { usage }),
|
|
518
522
|
};
|
|
519
523
|
|
|
520
|
-
// Build new message array: keep the
|
|
521
|
-
const newMessages: Message[] = [
|
|
524
|
+
// Build new message array: keep the compacted message and last 3 messages
|
|
525
|
+
const newMessages: Message[] = [compactMessage, ...lastThreeMessages];
|
|
522
526
|
|
|
523
527
|
// Update sessionId and parentSessionId
|
|
524
528
|
const oldSessionId = this.sessionId;
|
|
@@ -539,15 +543,15 @@ export class MessageManager {
|
|
|
539
543
|
this.addPathsFromMessage(message);
|
|
540
544
|
}
|
|
541
545
|
|
|
542
|
-
// Scan
|
|
546
|
+
// Scan compactedContent for file mentions
|
|
543
547
|
const fileMentionRegex = /(?:^|\s)@([\w.\-/]+)/g;
|
|
544
548
|
let match;
|
|
545
|
-
while ((match = fileMentionRegex.exec(
|
|
549
|
+
while ((match = fileMentionRegex.exec(compactedContent)) !== null) {
|
|
546
550
|
this.touchFile(match[1]);
|
|
547
551
|
}
|
|
548
552
|
|
|
549
|
-
// Trigger
|
|
550
|
-
this.callbacks.
|
|
553
|
+
// Trigger compaction callback
|
|
554
|
+
this.callbacks.onCompactBlockAdded?.(compactedContent);
|
|
551
555
|
}
|
|
552
556
|
|
|
553
557
|
public addFileHistoryBlock(
|
|
@@ -861,7 +865,7 @@ export class MessageManager {
|
|
|
861
865
|
let targetSessionId = this.sessionId;
|
|
862
866
|
let targetIndexInSession = index;
|
|
863
867
|
|
|
864
|
-
// We need to be careful here because loadFullMessageThread might have removed "
|
|
868
|
+
// We need to be careful here because loadFullMessageThread might have removed "compact" blocks
|
|
865
869
|
// Let's re-calculate based on the actual messages returned.
|
|
866
870
|
// Actually, it's easier to just load sessions one by one again or keep track of counts.
|
|
867
871
|
|
|
@@ -883,19 +887,19 @@ export class MessageManager {
|
|
|
883
887
|
if (!sessionData) continue;
|
|
884
888
|
|
|
885
889
|
const sessionMessages = sessionData.messages;
|
|
886
|
-
// If this is not the first session in the thread, it might have a
|
|
890
|
+
// If this is not the first session in the thread, it might have a compact block at the start
|
|
887
891
|
// that was removed in getFullMessageThread.
|
|
888
|
-
const
|
|
889
|
-
(b) => b.type === "
|
|
892
|
+
const hasCompactBlock = sessionMessages[0]?.blocks.some(
|
|
893
|
+
(b) => b.type === "compact",
|
|
890
894
|
);
|
|
891
895
|
const effectiveMessages =
|
|
892
|
-
|
|
896
|
+
hasCompactBlock && sid !== sessionIds[0]
|
|
893
897
|
? sessionMessages.slice(1)
|
|
894
898
|
: sessionMessages;
|
|
895
899
|
|
|
896
900
|
if (remainingIndex < effectiveMessages.length) {
|
|
897
901
|
targetSessionId = sid;
|
|
898
|
-
targetIndexInSession =
|
|
902
|
+
targetIndexInSession = hasCompactBlock
|
|
899
903
|
? remainingIndex + 1
|
|
900
904
|
: remainingIndex;
|
|
901
905
|
break;
|
|
@@ -938,7 +942,7 @@ export class MessageManager {
|
|
|
938
942
|
|
|
939
943
|
// Update in-memory messages to the truncated session messages
|
|
940
944
|
// We do NOT include ancestor messages here to avoid exceeding context limits.
|
|
941
|
-
// The '
|
|
945
|
+
// The 'compact' block at the start of the session (if any) already summarizes them.
|
|
942
946
|
this.setMessages(newMessagesInSession);
|
|
943
947
|
|
|
944
948
|
// Update saved message count
|
|
@@ -1056,4 +1060,54 @@ export class MessageManager {
|
|
|
1056
1060
|
}
|
|
1057
1061
|
return result;
|
|
1058
1062
|
}
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* Extract skill invocations from tool blocks in a message.
|
|
1066
|
+
*/
|
|
1067
|
+
private extractSkillInvocationsFromMessage(message: Message): void {
|
|
1068
|
+
for (const block of message.blocks) {
|
|
1069
|
+
if (
|
|
1070
|
+
block.type === "tool" &&
|
|
1071
|
+
block.name === "Skill" &&
|
|
1072
|
+
block.stage === "end" &&
|
|
1073
|
+
block.parameters
|
|
1074
|
+
) {
|
|
1075
|
+
try {
|
|
1076
|
+
const params = JSON.parse(block.parameters) as Record<
|
|
1077
|
+
string,
|
|
1078
|
+
unknown
|
|
1079
|
+
>;
|
|
1080
|
+
const skillName = params.skill_name as string | undefined;
|
|
1081
|
+
if (skillName) {
|
|
1082
|
+
this.invokedSkills.set(skillName, {
|
|
1083
|
+
skillName,
|
|
1084
|
+
timestamp: Date.now(),
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
} catch {
|
|
1088
|
+
// Ignore parse errors
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
* Get recently invoked skill names, sorted by timestamp (newest first).
|
|
1096
|
+
* @param maxSkills - Maximum number of skills to return
|
|
1097
|
+
* @returns Array of skill names sorted by recency
|
|
1098
|
+
*/
|
|
1099
|
+
public getInvokedSkillNames(maxSkills = 10): string[] {
|
|
1100
|
+
const sorted = Array.from(this.invokedSkills.entries())
|
|
1101
|
+
.sort(([, a], [, b]) => b.timestamp - a.timestamp)
|
|
1102
|
+
.slice(0, maxSkills);
|
|
1103
|
+
|
|
1104
|
+
return sorted.map(([, { skillName }]) => skillName);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
/**
|
|
1108
|
+
* Clear all invoked skills (e.g., after compaction).
|
|
1109
|
+
*/
|
|
1110
|
+
public clearInvokedSkills(): void {
|
|
1111
|
+
this.invokedSkills.clear();
|
|
1112
|
+
}
|
|
1059
1113
|
}
|
package/src/prompts/index.ts
CHANGED
|
@@ -179,7 +179,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
|
|
179
179
|
|
|
180
180
|
export const DEFAULT_SYSTEM_PROMPT = BASE_SYSTEM_PROMPT;
|
|
181
181
|
|
|
182
|
-
export const
|
|
182
|
+
export const COMPACT_MESSAGES_SYSTEM_PROMPT = `You are continuing work on a software engineering task. Write a detailed continuation summary that will allow you (or another instance of yourself) to resume work efficiently in a future context window where the conversation history will be replaced with this summary.
|
|
183
183
|
|
|
184
184
|
First, write your analysis in <analysis> tags as a thinking scratchpad:
|
|
185
185
|
- Chronologically review the conversation
|
|
@@ -8,12 +8,12 @@ import {
|
|
|
8
8
|
InstalledPlugin,
|
|
9
9
|
InstalledPluginsRegistry,
|
|
10
10
|
MarketplaceManifest,
|
|
11
|
-
MarketplacePluginEntry,
|
|
12
11
|
MarketplaceSource,
|
|
13
12
|
} from "../types/marketplace.js";
|
|
14
13
|
import { GitService } from "./GitService.js";
|
|
15
14
|
import { ConfigurationService } from "./configurationService.js";
|
|
16
15
|
import type { MarketplaceConfig, Scope } from "../types/configuration.js";
|
|
16
|
+
import { logger } from "../utils/globalLogger.js";
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Marketplace Service
|
|
@@ -320,42 +320,19 @@ export class MarketplaceService {
|
|
|
320
320
|
await fs.rename(tmpPath, this.installedPluginsPath);
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
-
/**
|
|
324
|
-
* Finds the first existing marketplace manifest path.
|
|
325
|
-
* Prefers .wave-plugin/ for backward compatibility, falls back to .claude-plugin/.
|
|
326
|
-
* Returns null if neither exists.
|
|
327
|
-
*/
|
|
328
|
-
private async findMarketplaceManifestPath(
|
|
329
|
-
dir: string,
|
|
330
|
-
): Promise<string | null> {
|
|
331
|
-
const waveManifestPath = path.join(dir, ".wave-plugin", "marketplace.json");
|
|
332
|
-
const claudeManifestPath = path.join(
|
|
333
|
-
dir,
|
|
334
|
-
".claude-plugin",
|
|
335
|
-
"marketplace.json",
|
|
336
|
-
);
|
|
337
|
-
|
|
338
|
-
if (existsSync(waveManifestPath)) {
|
|
339
|
-
return waveManifestPath;
|
|
340
|
-
}
|
|
341
|
-
if (existsSync(claudeManifestPath)) {
|
|
342
|
-
return claudeManifestPath;
|
|
343
|
-
}
|
|
344
|
-
return null;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
323
|
/**
|
|
348
324
|
* Loads a marketplace manifest from a local path
|
|
349
325
|
*/
|
|
350
326
|
async loadMarketplaceManifest(
|
|
351
327
|
marketplacePath: string,
|
|
352
328
|
): Promise<MarketplaceManifest> {
|
|
353
|
-
const manifestPath =
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
329
|
+
const manifestPath = path.join(
|
|
330
|
+
marketplacePath,
|
|
331
|
+
".wave-plugin",
|
|
332
|
+
"marketplace.json",
|
|
333
|
+
);
|
|
334
|
+
if (!existsSync(manifestPath)) {
|
|
335
|
+
throw new Error(`Marketplace manifest not found at ${manifestPath}`);
|
|
359
336
|
}
|
|
360
337
|
const content = await fs.readFile(manifestPath, "utf-8");
|
|
361
338
|
const manifest = JSON.parse(content);
|
|
@@ -665,7 +642,7 @@ export class MarketplaceService {
|
|
|
665
642
|
(p) => p.name === plugin.name,
|
|
666
643
|
);
|
|
667
644
|
if (!pluginEntry) {
|
|
668
|
-
|
|
645
|
+
logger.warn(
|
|
669
646
|
`Plugin "${plugin.name}" no longer found in marketplace "${marketplace.name}". Uninstalling...`,
|
|
670
647
|
);
|
|
671
648
|
try {
|
|
@@ -767,74 +744,6 @@ export class MarketplaceService {
|
|
|
767
744
|
});
|
|
768
745
|
}
|
|
769
746
|
|
|
770
|
-
/**
|
|
771
|
-
* Resolves a plugin source into a consistent format for installation.
|
|
772
|
-
* Handles both string sources (local paths or git URLs) and object-style MarketplaceSource.
|
|
773
|
-
*/
|
|
774
|
-
private resolvePluginSource(
|
|
775
|
-
pluginEntry: MarketplacePluginEntry,
|
|
776
|
-
marketplacePath: string,
|
|
777
|
-
): { isGit: boolean; url?: string; ref?: string; localPath: string } {
|
|
778
|
-
const { source } = pluginEntry;
|
|
779
|
-
|
|
780
|
-
if (typeof source === "string") {
|
|
781
|
-
// String source: could be a git URL or a relative local path
|
|
782
|
-
const isGitUrl =
|
|
783
|
-
source.startsWith("http://") ||
|
|
784
|
-
source.startsWith("https://") ||
|
|
785
|
-
source.startsWith("git@") ||
|
|
786
|
-
source.startsWith("ssh://");
|
|
787
|
-
|
|
788
|
-
if (isGitUrl) {
|
|
789
|
-
let url = source;
|
|
790
|
-
let ref: string | undefined;
|
|
791
|
-
if (url.includes("#")) {
|
|
792
|
-
[url, ref] = url.split("#");
|
|
793
|
-
}
|
|
794
|
-
return { isGit: true, url, ref, localPath: "" };
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
// Relative local path
|
|
798
|
-
return { isGit: false, localPath: path.resolve(marketplacePath, source) };
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
// Object-style source
|
|
802
|
-
if (source.source === "git") {
|
|
803
|
-
return { isGit: true, url: source.url, ref: source.ref, localPath: "" };
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
if (source.source === "github") {
|
|
807
|
-
return {
|
|
808
|
-
isGit: true,
|
|
809
|
-
url: `https://github.com/${source.repo}.git`,
|
|
810
|
-
ref: source.ref,
|
|
811
|
-
localPath: "",
|
|
812
|
-
};
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
if (source.source === "url") {
|
|
816
|
-
let url = source.url;
|
|
817
|
-
let ref = source.ref;
|
|
818
|
-
if (url.includes("#")) {
|
|
819
|
-
[url, ref] = url.split("#");
|
|
820
|
-
}
|
|
821
|
-
return { isGit: true, url, ref, localPath: "" };
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
if (source.source === "directory") {
|
|
825
|
-
return {
|
|
826
|
-
isGit: false,
|
|
827
|
-
localPath: path.resolve(marketplacePath, source.path),
|
|
828
|
-
};
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
// Exhaustiveness: this should be unreachable given the union type
|
|
832
|
-
const _exhaustive: never = source;
|
|
833
|
-
throw new Error(
|
|
834
|
-
`Unsupported plugin source type: ${(_exhaustive as { source: string }).source}`,
|
|
835
|
-
);
|
|
836
|
-
}
|
|
837
|
-
|
|
838
747
|
/**
|
|
839
748
|
* Installs a plugin from a marketplace
|
|
840
749
|
*/
|
|
@@ -863,46 +772,36 @@ export class MarketplaceService {
|
|
|
863
772
|
);
|
|
864
773
|
}
|
|
865
774
|
|
|
866
|
-
const
|
|
775
|
+
const isGitSource =
|
|
776
|
+
pluginEntry.source.startsWith("http://") ||
|
|
777
|
+
pluginEntry.source.startsWith("https://") ||
|
|
778
|
+
pluginEntry.source.startsWith("git@") ||
|
|
779
|
+
pluginEntry.source.startsWith("ssh://");
|
|
867
780
|
|
|
868
781
|
let pluginSrcPath: string;
|
|
869
782
|
let tempCloneDir: string | undefined;
|
|
870
783
|
|
|
871
784
|
try {
|
|
872
|
-
if (
|
|
785
|
+
if (isGitSource) {
|
|
873
786
|
tempCloneDir = path.join(this.tmpDir, `clone-${Date.now()}`);
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
787
|
+
let url = pluginEntry.source;
|
|
788
|
+
let ref: string | undefined;
|
|
789
|
+
if (url.includes("#")) {
|
|
790
|
+
[url, ref] = url.split("#");
|
|
791
|
+
}
|
|
792
|
+
await this.gitService.clone(url, tempCloneDir, ref);
|
|
879
793
|
pluginSrcPath = tempCloneDir;
|
|
880
794
|
} else {
|
|
881
|
-
pluginSrcPath =
|
|
795
|
+
pluginSrcPath = path.resolve(marketplacePath, pluginEntry.source);
|
|
882
796
|
}
|
|
883
797
|
|
|
884
|
-
|
|
885
|
-
const wavePluginPath = path.join(
|
|
798
|
+
const pluginManifestPath = path.join(
|
|
886
799
|
pluginSrcPath,
|
|
887
800
|
".wave-plugin",
|
|
888
801
|
"plugin.json",
|
|
889
802
|
);
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
".claude-plugin",
|
|
893
|
-
"plugin.json",
|
|
894
|
-
);
|
|
895
|
-
|
|
896
|
-
if (existsSync(wavePluginPath)) {
|
|
897
|
-
pluginManifestPath = wavePluginPath;
|
|
898
|
-
} else if (existsSync(claudePluginPath)) {
|
|
899
|
-
pluginManifestPath = claudePluginPath;
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
if (!pluginManifestPath) {
|
|
903
|
-
throw new Error(
|
|
904
|
-
`Plugin manifest not found at ${pluginSrcPath}. Neither .wave-plugin/plugin.json nor .claude-plugin/plugin.json exists.`,
|
|
905
|
-
);
|
|
803
|
+
if (!existsSync(pluginManifestPath)) {
|
|
804
|
+
throw new Error(`Plugin manifest not found at ${pluginManifestPath}`);
|
|
906
805
|
}
|
|
907
806
|
|
|
908
807
|
const pluginManifestContent = await fs.readFile(
|
|
@@ -917,7 +816,7 @@ export class MarketplaceService {
|
|
|
917
816
|
`${pluginName}-${Date.now()}`,
|
|
918
817
|
);
|
|
919
818
|
try {
|
|
920
|
-
if (
|
|
819
|
+
if (isGitSource) {
|
|
921
820
|
await fs.rename(pluginSrcPath, tmpPluginDir);
|
|
922
821
|
tempCloneDir = undefined;
|
|
923
822
|
} else {
|
|
@@ -23,7 +23,7 @@ import * as fs from "fs";
|
|
|
23
23
|
import * as path from "path";
|
|
24
24
|
|
|
25
25
|
import {
|
|
26
|
-
|
|
26
|
+
COMPACT_MESSAGES_SYSTEM_PROMPT,
|
|
27
27
|
WEB_CONTENT_SYSTEM_PROMPT,
|
|
28
28
|
BTW_SYSTEM_PROMPT,
|
|
29
29
|
} from "../prompts/index.js";
|
|
@@ -751,7 +751,7 @@ async function processStreamingResponse(
|
|
|
751
751
|
return result;
|
|
752
752
|
}
|
|
753
753
|
|
|
754
|
-
export interface
|
|
754
|
+
export interface CompactMessagesOptions {
|
|
755
755
|
// Resolved configuration
|
|
756
756
|
gatewayConfig: GatewayConfig;
|
|
757
757
|
modelConfig: ModelConfig;
|
|
@@ -762,7 +762,7 @@ export interface CompressMessagesOptions {
|
|
|
762
762
|
model?: string;
|
|
763
763
|
}
|
|
764
764
|
|
|
765
|
-
export interface
|
|
765
|
+
export interface CompactMessagesResult {
|
|
766
766
|
content: string;
|
|
767
767
|
usage?: {
|
|
768
768
|
prompt_tokens: number;
|
|
@@ -771,9 +771,9 @@ export interface CompressMessagesResult {
|
|
|
771
771
|
};
|
|
772
772
|
}
|
|
773
773
|
|
|
774
|
-
export async function
|
|
775
|
-
options:
|
|
776
|
-
): Promise<
|
|
774
|
+
export async function compactMessages(
|
|
775
|
+
options: CompactMessagesOptions,
|
|
776
|
+
): Promise<CompactMessagesResult> {
|
|
777
777
|
const { gatewayConfig, modelConfig, messages, abortSignal } = options;
|
|
778
778
|
|
|
779
779
|
// Apply global 1 QPS rate limit
|
|
@@ -832,7 +832,7 @@ export async function compressMessages(
|
|
|
832
832
|
messages: [
|
|
833
833
|
{
|
|
834
834
|
role: "system",
|
|
835
|
-
content:
|
|
835
|
+
content: COMPACT_MESSAGES_SYSTEM_PROMPT,
|
|
836
836
|
},
|
|
837
837
|
...cleanedMessages,
|
|
838
838
|
{
|
|
@@ -849,7 +849,7 @@ export async function compressMessages(
|
|
|
849
849
|
const content = response.choices[0]?.message?.content?.trim();
|
|
850
850
|
if (!content) {
|
|
851
851
|
throw new Error(
|
|
852
|
-
"Failed to
|
|
852
|
+
"Failed to compact conversation history: Empty response from AI",
|
|
853
853
|
);
|
|
854
854
|
}
|
|
855
855
|
const usage = response.usage
|
|
@@ -866,10 +866,10 @@ export async function compressMessages(
|
|
|
866
866
|
};
|
|
867
867
|
} catch (error) {
|
|
868
868
|
if ((error as Error).name === "AbortError") {
|
|
869
|
-
logger.info("
|
|
870
|
-
throw new Error("
|
|
869
|
+
logger.info("Compaction request was aborted");
|
|
870
|
+
throw new Error("Compaction request was aborted");
|
|
871
871
|
}
|
|
872
|
-
logger.error("Failed to
|
|
872
|
+
logger.error("Failed to compact messages:", error);
|
|
873
873
|
throw error;
|
|
874
874
|
}
|
|
875
875
|
}
|
package/src/services/hook.ts
CHANGED
|
@@ -84,6 +84,23 @@ async function buildHookJsonInput(
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
// Add SessionStart-specific fields
|
|
88
|
+
if (context.event === "SessionStart") {
|
|
89
|
+
if (context.source !== undefined) {
|
|
90
|
+
jsonInput.source = context.source;
|
|
91
|
+
}
|
|
92
|
+
if (context.agentType !== undefined) {
|
|
93
|
+
jsonInput.agent_type = context.agentType;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Add SessionEnd-specific fields
|
|
98
|
+
if (context.event === "SessionEnd") {
|
|
99
|
+
if (context.endSource !== undefined) {
|
|
100
|
+
jsonInput.end_source = context.endSource;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
87
104
|
return jsonInput;
|
|
88
105
|
}
|
|
89
106
|
|
|
@@ -178,6 +178,38 @@ export class InitializationService {
|
|
|
178
178
|
// Don't throw error to prevent app startup failure
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
// Execute SessionStart hooks
|
|
182
|
+
try {
|
|
183
|
+
const phaseStart = performance.now();
|
|
184
|
+
const sessionStartResult = await hookManager.executeSessionStartHooks(
|
|
185
|
+
"startup",
|
|
186
|
+
messageManager.getSessionId(),
|
|
187
|
+
messageManager.getTranscriptPath(),
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
// Inject additionalContext as a meta user message (matches Claude Code)
|
|
191
|
+
if (sessionStartResult.additionalContext) {
|
|
192
|
+
messageManager.addUserMessage({
|
|
193
|
+
content: `<system-reminder>\nSessionStart hook additional context: ${sessionStartResult.additionalContext}\n</system-reminder>`,
|
|
194
|
+
isMeta: true,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Inject initialUserMessage as a meta user message
|
|
199
|
+
if (sessionStartResult.initialUserMessage) {
|
|
200
|
+
messageManager.addUserMessage({
|
|
201
|
+
content: sessionStartResult.initialUserMessage,
|
|
202
|
+
isMeta: true,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
logger?.debug(
|
|
207
|
+
`Initialization Phase [SessionStart Hooks] took ${(performance.now() - phaseStart).toFixed(2)}ms`,
|
|
208
|
+
);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
logger?.warn("SessionStart hooks execution failed:", error);
|
|
211
|
+
}
|
|
212
|
+
|
|
181
213
|
// Trigger WorktreeCreate hook if this is a new worktree
|
|
182
214
|
if (agentOptions.isNewWorktree && hookManager) {
|
|
183
215
|
try {
|
|
@@ -318,7 +350,7 @@ export class InitializationService {
|
|
|
318
350
|
if (sessionToRestore) {
|
|
319
351
|
messageManager.initializeFromSession(sessionToRestore);
|
|
320
352
|
|
|
321
|
-
// Update task manager with the root session ID to ensure continuity across
|
|
353
|
+
// Update task manager with the root session ID to ensure continuity across compactions
|
|
322
354
|
taskManager.setTaskListId(
|
|
323
355
|
sessionToRestore.rootSessionId || sessionToRestore.id,
|
|
324
356
|
);
|
|
@@ -183,7 +183,7 @@ export class InteractionService {
|
|
|
183
183
|
// 6. Initialize session state last
|
|
184
184
|
messageManager.initializeFromSession(sessionData);
|
|
185
185
|
|
|
186
|
-
// Update task manager with the root session ID to ensure continuity across
|
|
186
|
+
// Update task manager with the root session ID to ensure continuity across compactions
|
|
187
187
|
taskManager.setTaskListId(sessionData.rootSessionId || sessionData.id);
|
|
188
188
|
|
|
189
189
|
// 7. Load tasks for the restored session
|