@charming_groot/agent 0.1.0
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/agent-loop.d.ts +46 -0
- package/dist/agent-loop.d.ts.map +1 -0
- package/dist/agent-loop.js +139 -0
- package/dist/agent-loop.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/message-manager.d.ts +46 -0
- package/dist/message-manager.d.ts.map +1 -0
- package/dist/message-manager.js +152 -0
- package/dist/message-manager.js.map +1 -0
- package/dist/permission.d.ts +37 -0
- package/dist/permission.d.ts.map +1 -0
- package/dist/permission.js +62 -0
- package/dist/permission.js.map +1 -0
- package/dist/session-manager.d.ts +31 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/session-manager.js +101 -0
- package/dist/session-manager.js.map +1 -0
- package/dist/skill-tool.d.ts +38 -0
- package/dist/skill-tool.d.ts.map +1 -0
- package/dist/skill-tool.js +112 -0
- package/dist/skill-tool.js.map +1 -0
- package/dist/sub-agent-tool.d.ts +39 -0
- package/dist/sub-agent-tool.d.ts.map +1 -0
- package/dist/sub-agent-tool.js +80 -0
- package/dist/sub-agent-tool.js.map +1 -0
- package/dist/token-counter.d.ts +16 -0
- package/dist/token-counter.d.ts.map +1 -0
- package/dist/token-counter.js +69 -0
- package/dist/token-counter.js.map +1 -0
- package/dist/tool-dispatcher.d.ts +15 -0
- package/dist/tool-dispatcher.d.ts.map +1 -0
- package/dist/tool-dispatcher.js +94 -0
- package/dist/tool-dispatcher.js.map +1 -0
- package/package.json +34 -0
- package/src/agent-loop.ts +210 -0
- package/src/index.ts +19 -0
- package/src/message-manager.ts +184 -0
- package/src/permission.ts +104 -0
- package/src/session-manager.ts +121 -0
- package/src/skill-tool.ts +155 -0
- package/src/sub-agent-tool.ts +122 -0
- package/src/token-counter.ts +79 -0
- package/src/tool-dispatcher.ts +124 -0
- package/tests/agent-loop.test.ts +372 -0
- package/tests/message-manager-new.test.ts +204 -0
- package/tests/message-manager.test.ts +195 -0
- package/tests/permission.test.ts +148 -0
- package/tests/session-manager.test.ts +106 -0
- package/tests/skill-tool.test.ts +119 -0
- package/tests/sub-agent-tool.test.ts +198 -0
- package/tests/token-counter.test.ts +77 -0
- package/tests/tool-dispatcher.test.ts +181 -0
- package/tsconfig.json +9 -0
- package/vitest.config.ts +17 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ILlmProvider, ITool, AgentConfig } from '@charming_groot/core';
|
|
2
|
+
import { Registry, RunContext, EventBus } from '@charming_groot/core';
|
|
3
|
+
import { type PermissionHandler } from './permission.js';
|
|
4
|
+
/**
|
|
5
|
+
* Callback that builds/rebuilds the system prompt dynamically.
|
|
6
|
+
* Called before each LLM iteration so the prompt can reflect
|
|
7
|
+
* current state (open files, cwd, etc.) without a hard dependency
|
|
8
|
+
* on @core/context-engine.
|
|
9
|
+
*/
|
|
10
|
+
export type SystemPromptBuilder = (context: RunContext) => string | Promise<string>;
|
|
11
|
+
export interface AgentLoopOptions {
|
|
12
|
+
provider: ILlmProvider;
|
|
13
|
+
toolRegistry: Registry<ITool>;
|
|
14
|
+
config: AgentConfig;
|
|
15
|
+
permissionHandler?: PermissionHandler;
|
|
16
|
+
eventBus?: EventBus;
|
|
17
|
+
streaming?: boolean;
|
|
18
|
+
/** Dynamic system prompt builder — takes precedence over config.systemPrompt */
|
|
19
|
+
systemPromptBuilder?: SystemPromptBuilder;
|
|
20
|
+
}
|
|
21
|
+
export interface AgentResult {
|
|
22
|
+
readonly content: string;
|
|
23
|
+
readonly runId: string;
|
|
24
|
+
readonly iterations: number;
|
|
25
|
+
readonly aborted: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare class AgentLoop {
|
|
28
|
+
private readonly provider;
|
|
29
|
+
private readonly toolDispatcher;
|
|
30
|
+
private readonly messageManager;
|
|
31
|
+
private readonly context;
|
|
32
|
+
private readonly maxIterations;
|
|
33
|
+
private readonly logger;
|
|
34
|
+
private readonly streaming;
|
|
35
|
+
private readonly systemPromptBuilder?;
|
|
36
|
+
private iterations;
|
|
37
|
+
constructor(options: AgentLoopOptions);
|
|
38
|
+
get runId(): string;
|
|
39
|
+
get eventBus(): EventBus;
|
|
40
|
+
run(userMessage: string): Promise<AgentResult>;
|
|
41
|
+
abort(reason?: string): void;
|
|
42
|
+
getContext(): RunContext;
|
|
43
|
+
private streamResponse;
|
|
44
|
+
private getToolDescriptions;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=agent-loop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,KAAK,EACL,WAAW,EAKZ,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,EAGT,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAE5E;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,UAAU,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAEpF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,YAAY,CAAC;IACvB,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9B,MAAM,EAAE,WAAW,CAAC;IACpB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gFAAgF;IAChF,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;CAC3C;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAsB;IAC3D,OAAO,CAAC,UAAU,CAAK;gBAEX,OAAO,EAAE,gBAAgB;IAmBrC,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,QAAQ,IAAI,QAAQ,CAEvB;IAEK,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IA2FpD,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAI5B,UAAU,IAAI,UAAU;YAIV,cAAc;IAuB5B,OAAO,CAAC,mBAAmB;CAG5B"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { RunContext, EventBus, AbortError, createChildLogger, } from '@charming_groot/core';
|
|
2
|
+
import { MessageManager } from './message-manager.js';
|
|
3
|
+
import { ToolDispatcher } from './tool-dispatcher.js';
|
|
4
|
+
import { PermissionManager } from './permission.js';
|
|
5
|
+
export class AgentLoop {
|
|
6
|
+
provider;
|
|
7
|
+
toolDispatcher;
|
|
8
|
+
messageManager;
|
|
9
|
+
context;
|
|
10
|
+
maxIterations;
|
|
11
|
+
logger;
|
|
12
|
+
streaming;
|
|
13
|
+
systemPromptBuilder;
|
|
14
|
+
iterations = 0;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
const eventBus = options.eventBus ?? new EventBus();
|
|
17
|
+
this.context = new RunContext(options.config, eventBus);
|
|
18
|
+
this.provider = options.provider;
|
|
19
|
+
this.messageManager = new MessageManager();
|
|
20
|
+
this.maxIterations = options.config.maxIterations;
|
|
21
|
+
this.logger = createChildLogger('agent-loop');
|
|
22
|
+
this.streaming = options.streaming ?? false;
|
|
23
|
+
this.systemPromptBuilder = options.systemPromptBuilder;
|
|
24
|
+
const permissionManager = new PermissionManager(options.permissionHandler);
|
|
25
|
+
this.toolDispatcher = new ToolDispatcher(options.toolRegistry, permissionManager);
|
|
26
|
+
// Static system prompt used only when no dynamic builder is provided
|
|
27
|
+
if (!this.systemPromptBuilder && options.config.systemPrompt) {
|
|
28
|
+
this.messageManager.addSystemMessage(options.config.systemPrompt);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
get runId() {
|
|
32
|
+
return this.context.runId;
|
|
33
|
+
}
|
|
34
|
+
get eventBus() {
|
|
35
|
+
return this.context.eventBus;
|
|
36
|
+
}
|
|
37
|
+
async run(userMessage) {
|
|
38
|
+
this.messageManager.addUserMessage(userMessage);
|
|
39
|
+
const agentStartedAt = Date.now();
|
|
40
|
+
this.context.eventBus.emit('agent:start', {
|
|
41
|
+
runId: this.context.runId,
|
|
42
|
+
model: this.context.config.provider.model,
|
|
43
|
+
startedAt: agentStartedAt,
|
|
44
|
+
});
|
|
45
|
+
this.iterations = 0;
|
|
46
|
+
try {
|
|
47
|
+
let lastContent = '';
|
|
48
|
+
while (this.iterations < this.maxIterations) {
|
|
49
|
+
if (this.context.isAborted) {
|
|
50
|
+
throw new AbortError('Agent loop aborted');
|
|
51
|
+
}
|
|
52
|
+
this.iterations++;
|
|
53
|
+
this.logger.debug({ iteration: this.iterations }, 'Starting iteration');
|
|
54
|
+
// Rebuild system prompt dynamically if builder is provided
|
|
55
|
+
if (this.systemPromptBuilder) {
|
|
56
|
+
const prompt = await this.systemPromptBuilder(this.context);
|
|
57
|
+
this.messageManager.setSystemMessage(prompt);
|
|
58
|
+
}
|
|
59
|
+
const compressed = this.messageManager.compressIfNeeded();
|
|
60
|
+
if (compressed > 0) {
|
|
61
|
+
this.logger.info({ compressed }, 'History compressed');
|
|
62
|
+
}
|
|
63
|
+
const toolDescriptions = this.getToolDescriptions();
|
|
64
|
+
const messages = this.messageManager.getMessages();
|
|
65
|
+
this.context.eventBus.emit('llm:request', {
|
|
66
|
+
runId: this.context.runId,
|
|
67
|
+
messages,
|
|
68
|
+
});
|
|
69
|
+
const llmStartedAt = Date.now();
|
|
70
|
+
const response = this.streaming
|
|
71
|
+
? await this.streamResponse(messages, toolDescriptions)
|
|
72
|
+
: await this.provider.chat(messages, toolDescriptions);
|
|
73
|
+
this.context.eventBus.emit('llm:response', {
|
|
74
|
+
runId: this.context.runId,
|
|
75
|
+
response,
|
|
76
|
+
durationMs: Date.now() - llmStartedAt,
|
|
77
|
+
model: this.context.config.provider.model,
|
|
78
|
+
});
|
|
79
|
+
lastContent = response.content;
|
|
80
|
+
if (response.stopReason === 'end_turn' || response.toolCalls.length === 0) {
|
|
81
|
+
this.messageManager.addAssistantMessage(response.content);
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
// Tool use flow
|
|
85
|
+
this.messageManager.addAssistantMessage(response.content, response.toolCalls);
|
|
86
|
+
const results = await this.toolDispatcher.dispatchAll(response.toolCalls, this.context);
|
|
87
|
+
this.messageManager.addToolResults(results);
|
|
88
|
+
}
|
|
89
|
+
const aborted = this.context.isAborted;
|
|
90
|
+
this.context.eventBus.emit('agent:end', {
|
|
91
|
+
runId: this.context.runId,
|
|
92
|
+
reason: aborted ? 'aborted' : 'complete',
|
|
93
|
+
durationMs: Date.now() - agentStartedAt,
|
|
94
|
+
iterations: this.iterations,
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
content: lastContent,
|
|
98
|
+
runId: this.context.runId,
|
|
99
|
+
iterations: this.iterations,
|
|
100
|
+
aborted,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
this.context.eventBus.emit('agent:error', {
|
|
105
|
+
runId: this.context.runId,
|
|
106
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
107
|
+
});
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
abort(reason) {
|
|
112
|
+
this.context.abort(reason);
|
|
113
|
+
}
|
|
114
|
+
getContext() {
|
|
115
|
+
return this.context;
|
|
116
|
+
}
|
|
117
|
+
async streamResponse(messages, tools) {
|
|
118
|
+
let finalResponse;
|
|
119
|
+
for await (const event of this.provider.stream(messages, tools)) {
|
|
120
|
+
if (event.type === 'text_delta' && event.content) {
|
|
121
|
+
this.context.eventBus.emit('llm:stream', {
|
|
122
|
+
runId: this.context.runId,
|
|
123
|
+
chunk: event.content,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
else if (event.type === 'done' && event.response) {
|
|
127
|
+
finalResponse = event.response;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (!finalResponse) {
|
|
131
|
+
throw new Error('Stream ended without a final response');
|
|
132
|
+
}
|
|
133
|
+
return finalResponse;
|
|
134
|
+
}
|
|
135
|
+
getToolDescriptions() {
|
|
136
|
+
return this.toolDispatcher.getToolDescriptions();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=agent-loop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-loop.js","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { AgentLoop } from './agent-loop.js';
|
|
2
|
+
export type { AgentLoopOptions, AgentResult, SystemPromptBuilder } from './agent-loop.js';
|
|
3
|
+
export { MessageManager } from './message-manager.js';
|
|
4
|
+
export type { CompressionConfig } from './message-manager.js';
|
|
5
|
+
export { countTextTokens, countMessageTokens, countHistoryTokens } from './token-counter.js';
|
|
6
|
+
export { ToolDispatcher } from './tool-dispatcher.js';
|
|
7
|
+
export { PermissionManager } from './permission.js';
|
|
8
|
+
export type { PermissionHandler, PermissionDecision, ApprovalLevel, PersistApprovalCallback, } from './permission.js';
|
|
9
|
+
export { SubAgentTool } from './sub-agent-tool.js';
|
|
10
|
+
export type { SubAgentToolConfig } from './sub-agent-tool.js';
|
|
11
|
+
export { SkillTool } from './skill-tool.js';
|
|
12
|
+
export type { SkillEntry, SkillProvider } from './skill-tool.js';
|
|
13
|
+
export { SessionManager } from './session-manager.js';
|
|
14
|
+
export type { SessionMeta } from './session-manager.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACb,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { AgentLoop } from './agent-loop.js';
|
|
2
|
+
export { MessageManager } from './message-manager.js';
|
|
3
|
+
export { countTextTokens, countMessageTokens, countHistoryTokens } from './token-counter.js';
|
|
4
|
+
export { ToolDispatcher } from './tool-dispatcher.js';
|
|
5
|
+
export { PermissionManager } from './permission.js';
|
|
6
|
+
export { SubAgentTool } from './sub-agent-tool.js';
|
|
7
|
+
export { SkillTool } from './skill-tool.js';
|
|
8
|
+
export { SessionManager } from './session-manager.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAOpD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Message, ToolCall, ToolResult } from '@charming_groot/core';
|
|
2
|
+
export interface CompressionConfig {
|
|
3
|
+
/** Maximum tokens in history before compression triggers (default: 100_000) */
|
|
4
|
+
maxHistoryTokens?: number;
|
|
5
|
+
/** Number of recent non-system messages to keep intact (default: 10) */
|
|
6
|
+
keepRecentMessages?: number;
|
|
7
|
+
/** Truncate individual message content to this length in summary (default: 300) */
|
|
8
|
+
summaryContentLength?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class MessageManager {
|
|
11
|
+
private readonly messages;
|
|
12
|
+
private readonly maxHistoryTokens;
|
|
13
|
+
private readonly keepRecentMessages;
|
|
14
|
+
private readonly summaryContentLength;
|
|
15
|
+
constructor(config?: CompressionConfig);
|
|
16
|
+
addSystemMessage(content: string): void;
|
|
17
|
+
setSystemMessage(content: string): void;
|
|
18
|
+
addUserMessage(content: string): void;
|
|
19
|
+
addAssistantMessage(content: string, toolCalls?: readonly ToolCall[]): void;
|
|
20
|
+
addToolResults(results: ReadonlyMap<string, ToolResult>): void;
|
|
21
|
+
getMessages(): readonly Message[];
|
|
22
|
+
getLastMessage(): Message | undefined;
|
|
23
|
+
get messageCount(): number;
|
|
24
|
+
/** Exact token count using js-tiktoken. */
|
|
25
|
+
get totalTokens(): number;
|
|
26
|
+
clear(): void;
|
|
27
|
+
serialize(): string;
|
|
28
|
+
restore(json: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Compress history if it exceeds the token budget.
|
|
31
|
+
*
|
|
32
|
+
* Strategy:
|
|
33
|
+
* 1. Always keep all system messages.
|
|
34
|
+
* 2. Keep the last `keepRecentMessages` non-system messages intact.
|
|
35
|
+
* 3. Summarize older messages into a structured digest that preserves:
|
|
36
|
+
* - what the user asked
|
|
37
|
+
* - what tools were called and whether they succeeded
|
|
38
|
+
* - key assistant conclusions
|
|
39
|
+
*
|
|
40
|
+
* Returns the number of messages compressed (0 = no compression needed).
|
|
41
|
+
*/
|
|
42
|
+
compressIfNeeded(): number;
|
|
43
|
+
private buildDigest;
|
|
44
|
+
}
|
|
45
|
+
export { countMessageTokens, countHistoryTokens } from './token-counter.js';
|
|
46
|
+
//# sourceMappingURL=message-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-manager.d.ts","sourceRoot":"","sources":["../src/message-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAG1E,MAAM,WAAW,iBAAiB;IAChC,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wEAAwE;IACxE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mFAAmF;IACnF,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;gBAElC,MAAM,GAAE,iBAAsB;IAM1C,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIvC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IASvC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIrC,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,SAAS,QAAQ,EAAE,GAAG,IAAI;IAQ3E,cAAc,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,IAAI;IAU9D,WAAW,IAAI,SAAS,OAAO,EAAE;IAIjC,cAAc,IAAI,OAAO,GAAG,SAAS;IAIrC,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,2CAA2C;IAC3C,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,KAAK,IAAI,IAAI;IAIb,SAAS,IAAI,MAAM;IAInB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAkB3B;;;;;;;;;;;;OAYG;IACH,gBAAgB,IAAI,MAAM;IA2B1B,OAAO,CAAC,WAAW;CAkCpB;AAOD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { countHistoryTokens } from './token-counter.js';
|
|
2
|
+
export class MessageManager {
|
|
3
|
+
messages = [];
|
|
4
|
+
maxHistoryTokens;
|
|
5
|
+
keepRecentMessages;
|
|
6
|
+
summaryContentLength;
|
|
7
|
+
constructor(config = {}) {
|
|
8
|
+
this.maxHistoryTokens = config.maxHistoryTokens ?? 100_000;
|
|
9
|
+
this.keepRecentMessages = config.keepRecentMessages ?? 10;
|
|
10
|
+
this.summaryContentLength = config.summaryContentLength ?? 300;
|
|
11
|
+
}
|
|
12
|
+
addSystemMessage(content) {
|
|
13
|
+
this.messages.push({ role: 'system', content });
|
|
14
|
+
}
|
|
15
|
+
setSystemMessage(content) {
|
|
16
|
+
const idx = this.messages.findIndex(m => m.role === 'system');
|
|
17
|
+
if (idx >= 0) {
|
|
18
|
+
this.messages[idx] = { role: 'system', content };
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
this.messages.unshift({ role: 'system', content });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
addUserMessage(content) {
|
|
25
|
+
this.messages.push({ role: 'user', content });
|
|
26
|
+
}
|
|
27
|
+
addAssistantMessage(content, toolCalls) {
|
|
28
|
+
this.messages.push({
|
|
29
|
+
role: 'assistant',
|
|
30
|
+
content,
|
|
31
|
+
toolCalls: toolCalls ? [...toolCalls] : undefined,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
addToolResults(results) {
|
|
35
|
+
const toolResults = [...results.entries()].map(([toolCallId, result]) => ({
|
|
36
|
+
toolCallId,
|
|
37
|
+
content: result.success
|
|
38
|
+
? result.output
|
|
39
|
+
: `Error: ${result.error ?? 'Unknown error'}`,
|
|
40
|
+
}));
|
|
41
|
+
this.messages.push({ role: 'user', content: '', toolResults });
|
|
42
|
+
}
|
|
43
|
+
getMessages() {
|
|
44
|
+
return [...this.messages];
|
|
45
|
+
}
|
|
46
|
+
getLastMessage() {
|
|
47
|
+
return this.messages[this.messages.length - 1];
|
|
48
|
+
}
|
|
49
|
+
get messageCount() {
|
|
50
|
+
return this.messages.length;
|
|
51
|
+
}
|
|
52
|
+
/** Exact token count using js-tiktoken. */
|
|
53
|
+
get totalTokens() {
|
|
54
|
+
return countHistoryTokens(this.messages);
|
|
55
|
+
}
|
|
56
|
+
clear() {
|
|
57
|
+
this.messages.length = 0;
|
|
58
|
+
}
|
|
59
|
+
serialize() {
|
|
60
|
+
return JSON.stringify(this.messages);
|
|
61
|
+
}
|
|
62
|
+
restore(json) {
|
|
63
|
+
const parsed = JSON.parse(json);
|
|
64
|
+
if (!Array.isArray(parsed)) {
|
|
65
|
+
throw new Error('Invalid serialized messages: expected an array');
|
|
66
|
+
}
|
|
67
|
+
this.messages.length = 0;
|
|
68
|
+
for (const item of parsed) {
|
|
69
|
+
if (typeof item === 'object' && item !== null &&
|
|
70
|
+
'role' in item && 'content' in item &&
|
|
71
|
+
typeof item.role === 'string' &&
|
|
72
|
+
typeof item.content === 'string') {
|
|
73
|
+
this.messages.push(item);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Compress history if it exceeds the token budget.
|
|
79
|
+
*
|
|
80
|
+
* Strategy:
|
|
81
|
+
* 1. Always keep all system messages.
|
|
82
|
+
* 2. Keep the last `keepRecentMessages` non-system messages intact.
|
|
83
|
+
* 3. Summarize older messages into a structured digest that preserves:
|
|
84
|
+
* - what the user asked
|
|
85
|
+
* - what tools were called and whether they succeeded
|
|
86
|
+
* - key assistant conclusions
|
|
87
|
+
*
|
|
88
|
+
* Returns the number of messages compressed (0 = no compression needed).
|
|
89
|
+
*/
|
|
90
|
+
compressIfNeeded() {
|
|
91
|
+
if (this.totalTokens <= this.maxHistoryTokens)
|
|
92
|
+
return 0;
|
|
93
|
+
const system = [];
|
|
94
|
+
const rest = [];
|
|
95
|
+
for (const msg of this.messages) {
|
|
96
|
+
if (msg.role === 'system')
|
|
97
|
+
system.push(msg);
|
|
98
|
+
else
|
|
99
|
+
rest.push(msg);
|
|
100
|
+
}
|
|
101
|
+
const keepCount = Math.min(this.keepRecentMessages, rest.length);
|
|
102
|
+
const toSummarize = rest.slice(0, rest.length - keepCount);
|
|
103
|
+
const toKeep = rest.slice(rest.length - keepCount);
|
|
104
|
+
if (toSummarize.length === 0)
|
|
105
|
+
return 0;
|
|
106
|
+
const summaryMessage = {
|
|
107
|
+
role: 'user',
|
|
108
|
+
content: this.buildDigest(toSummarize),
|
|
109
|
+
};
|
|
110
|
+
this.messages.length = 0;
|
|
111
|
+
this.messages.push(...system, summaryMessage, ...toKeep);
|
|
112
|
+
return toSummarize.length;
|
|
113
|
+
}
|
|
114
|
+
buildDigest(messages) {
|
|
115
|
+
const maxLen = this.summaryContentLength;
|
|
116
|
+
const lines = [
|
|
117
|
+
`[Context summary — ${messages.length} earlier messages compressed]`,
|
|
118
|
+
'',
|
|
119
|
+
];
|
|
120
|
+
for (const msg of messages) {
|
|
121
|
+
if (msg.role === 'user' && !msg.toolResults) {
|
|
122
|
+
const text = truncate(msg.content, maxLen);
|
|
123
|
+
if (text)
|
|
124
|
+
lines.push(`User: ${text}`);
|
|
125
|
+
}
|
|
126
|
+
if (msg.role === 'assistant') {
|
|
127
|
+
if (msg.content) {
|
|
128
|
+
lines.push(`Assistant: ${truncate(msg.content, maxLen)}`);
|
|
129
|
+
}
|
|
130
|
+
if (msg.toolCalls?.length) {
|
|
131
|
+
for (const tc of msg.toolCalls) {
|
|
132
|
+
lines.push(` → ${tc.name}(${truncate(tc.arguments, 120)})`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (msg.toolResults?.length) {
|
|
137
|
+
for (const tr of msg.toolResults) {
|
|
138
|
+
lines.push(` ← result: ${truncate(tr.content, 120)}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
lines.push('', '[End of summary — conversation continues below]');
|
|
143
|
+
return lines.join('\n');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function truncate(text, maxLen) {
|
|
147
|
+
if (!text)
|
|
148
|
+
return '';
|
|
149
|
+
return text.length <= maxLen ? text : text.slice(0, maxLen) + '…';
|
|
150
|
+
}
|
|
151
|
+
export { countMessageTokens, countHistoryTokens } from './token-counter.js';
|
|
152
|
+
//# sourceMappingURL=message-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-manager.js","sourceRoot":"","sources":["../src/message-manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAWxD,MAAM,OAAO,cAAc;IACR,QAAQ,GAAc,EAAE,CAAC;IACzB,gBAAgB,CAAS;IACzB,kBAAkB,CAAS;IAC3B,oBAAoB,CAAS;IAE9C,YAAY,SAA4B,EAAE;QACxC,IAAI,CAAC,gBAAgB,GAAO,MAAM,CAAC,gBAAgB,IAAQ,OAAO,CAAC;QACnE,IAAI,CAAC,kBAAkB,GAAK,MAAM,CAAC,kBAAkB,IAAM,EAAE,CAAC;QAC9D,IAAI,CAAC,oBAAoB,GAAG,MAAM,CAAC,oBAAoB,IAAI,GAAG,CAAC;IACjE,CAAC;IAED,gBAAgB,CAAC,OAAe;QAC9B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,gBAAgB,CAAC,OAAe;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;QAC9D,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;YACb,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;QACnD,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAED,cAAc,CAAC,OAAe;QAC5B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,mBAAmB,CAAC,OAAe,EAAE,SAA+B;QAClE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YACjB,IAAI,EAAE,WAAW;YACjB,OAAO;YACP,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;SAClD,CAAC,CAAC;IACL,CAAC;IAED,cAAc,CAAC,OAAwC;QACrD,MAAM,WAAW,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;YACxE,UAAU;YACV,OAAO,EAAE,MAAM,CAAC,OAAO;gBACrB,CAAC,CAAC,MAAM,CAAC,MAAM;gBACf,CAAC,CAAC,UAAU,MAAM,CAAC,KAAK,IAAI,eAAe,EAAE;SAChD,CAAC,CAAC,CAAC;QACJ,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,WAAW;QACT,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAED,cAAc;QACZ,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;IAC9B,CAAC;IAED,2CAA2C;IAC3C,IAAI,WAAW;QACb,OAAO,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK;QACH,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,CAAC,IAAY;QAClB,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACpE,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QACzB,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;YAC1B,IACE,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;gBACzC,MAAM,IAAI,IAAI,IAAI,SAAS,IAAI,IAAI;gBACnC,OAAQ,IAAgB,CAAC,IAAI,KAAK,QAAQ;gBAC1C,OAAQ,IAAgB,CAAC,OAAO,KAAK,QAAQ,EAC7C,CAAC;gBACD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAe,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,gBAAgB;QACd,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,gBAAgB;YAAE,OAAO,CAAC,CAAC;QAExD,MAAM,MAAM,GAAc,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAgB,EAAE,CAAC;QAE7B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChC,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;;gBACvC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtB,CAAC;QAED,MAAM,SAAS,GAAK,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACnE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;QAC3D,MAAM,MAAM,GAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;QAExD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QAEvC,MAAM,cAAc,GAAY;YAC9B,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC;SACvC,CAAC;QAEF,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,CAAC,CAAC;QACzD,OAAO,WAAW,CAAC,MAAM,CAAC;IAC5B,CAAC;IAEO,WAAW,CAAC,QAAmB;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,oBAAoB,CAAC;QACzC,MAAM,KAAK,GAAa;YACtB,sBAAsB,QAAQ,CAAC,MAAM,+BAA+B;YACpE,EAAE;SACH,CAAC;QAEF,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC3B,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBAC5C,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAC3C,IAAI,IAAI;oBAAE,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;YACxC,CAAC;YAED,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBAC7B,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;oBAChB,KAAK,CAAC,IAAI,CAAC,cAAc,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC5D,CAAC;gBACD,IAAI,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;oBAC1B,KAAK,MAAM,EAAE,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;wBAC/B,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,IAAI,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;oBAC/D,CAAC;gBACH,CAAC;YACH,CAAC;YAED,IAAI,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;gBAC5B,KAAK,MAAM,EAAE,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;oBACjC,KAAK,CAAC,IAAI,CAAC,eAAe,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;gBACzD,CAAC;YACH,CAAC;QACH,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,iDAAiD,CAAC,CAAC;QAClE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;CACF;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,MAAc;IAC5C,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,OAAO,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC;AACpE,CAAC;AAED,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ITool, JsonObject } from '@charming_groot/core';
|
|
2
|
+
/**
|
|
3
|
+
* Approval level returned by PermissionHandler.
|
|
4
|
+
*
|
|
5
|
+
* - 'once' — allow this single invocation only
|
|
6
|
+
* - 'session' — allow for the rest of this session (cached in allowedTools)
|
|
7
|
+
* - 'always' — allow permanently (caller is responsible for persistence)
|
|
8
|
+
* - 'deny' — block this invocation
|
|
9
|
+
*/
|
|
10
|
+
export type ApprovalLevel = 'once' | 'session' | 'always' | 'deny';
|
|
11
|
+
/**
|
|
12
|
+
* Decision returned by PermissionHandler.
|
|
13
|
+
* Can be a simple boolean (backward-compatible) or an ApprovalLevel.
|
|
14
|
+
*/
|
|
15
|
+
export type PermissionDecision = boolean | ApprovalLevel;
|
|
16
|
+
/**
|
|
17
|
+
* Callback that decides whether a tool invocation is allowed.
|
|
18
|
+
* Receives the tool name and the parsed parameters so the handler
|
|
19
|
+
* can make context-aware decisions (e.g., block `rm -rf /`).
|
|
20
|
+
*/
|
|
21
|
+
export type PermissionHandler = (toolName: string, params: JsonObject) => Promise<PermissionDecision>;
|
|
22
|
+
/** Callback invoked when a tool is approved with 'always' level */
|
|
23
|
+
export type PersistApprovalCallback = (toolName: string) => void;
|
|
24
|
+
export declare class PermissionManager {
|
|
25
|
+
private readonly handler;
|
|
26
|
+
private readonly sessionAllowed;
|
|
27
|
+
private readonly alwaysAllowed;
|
|
28
|
+
private readonly onPersist;
|
|
29
|
+
constructor(handler?: PermissionHandler, onPersist?: PersistApprovalCallback);
|
|
30
|
+
checkPermission(tool: ITool, params?: JsonObject): Promise<boolean>;
|
|
31
|
+
allowTool(toolName: string, level?: 'session' | 'always'): void;
|
|
32
|
+
revokeTool(toolName: string): void;
|
|
33
|
+
isAllowed(toolName: string): boolean;
|
|
34
|
+
clearSession(): void;
|
|
35
|
+
clearAll(): void;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=permission.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission.d.ts","sourceRoot":"","sources":["../src/permission.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAE9D;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEnE;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,aAAa,CAAC;AAEzD;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC9B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,UAAU,KACf,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAEjC,mEAAmE;AACnE,MAAM,MAAM,uBAAuB,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;AAUjE,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsC;gBAG9D,OAAO,CAAC,EAAE,iBAAiB,EAC3B,SAAS,CAAC,EAAE,uBAAuB;IAM/B,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,GAAE,UAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAyB7E,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,GAAE,SAAS,GAAG,QAAoB,GAAG,IAAI;IAQ1E,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKlC,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIpC,YAAY,IAAI,IAAI;IAIpB,QAAQ,IAAI,IAAI;CAIjB"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const AUTO_APPROVE = async () => true;
|
|
2
|
+
function normalizeDecision(decision) {
|
|
3
|
+
if (decision === true)
|
|
4
|
+
return 'session';
|
|
5
|
+
if (decision === false)
|
|
6
|
+
return 'deny';
|
|
7
|
+
return decision;
|
|
8
|
+
}
|
|
9
|
+
export class PermissionManager {
|
|
10
|
+
handler;
|
|
11
|
+
sessionAllowed = new Set();
|
|
12
|
+
alwaysAllowed = new Set();
|
|
13
|
+
onPersist;
|
|
14
|
+
constructor(handler, onPersist) {
|
|
15
|
+
this.handler = handler ?? AUTO_APPROVE;
|
|
16
|
+
this.onPersist = onPersist;
|
|
17
|
+
}
|
|
18
|
+
async checkPermission(tool, params = {}) {
|
|
19
|
+
if (!tool.requiresPermission) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
if (this.alwaysAllowed.has(tool.name) || this.sessionAllowed.has(tool.name)) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
const raw = await this.handler(tool.name, params);
|
|
26
|
+
const level = normalizeDecision(raw);
|
|
27
|
+
if (level === 'deny')
|
|
28
|
+
return false;
|
|
29
|
+
if (level === 'session') {
|
|
30
|
+
this.sessionAllowed.add(tool.name);
|
|
31
|
+
}
|
|
32
|
+
else if (level === 'always') {
|
|
33
|
+
this.alwaysAllowed.add(tool.name);
|
|
34
|
+
this.onPersist?.(tool.name);
|
|
35
|
+
}
|
|
36
|
+
// 'once' — no caching, just allow this time
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
allowTool(toolName, level = 'session') {
|
|
40
|
+
if (level === 'always') {
|
|
41
|
+
this.alwaysAllowed.add(toolName);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
this.sessionAllowed.add(toolName);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
revokeTool(toolName) {
|
|
48
|
+
this.sessionAllowed.delete(toolName);
|
|
49
|
+
this.alwaysAllowed.delete(toolName);
|
|
50
|
+
}
|
|
51
|
+
isAllowed(toolName) {
|
|
52
|
+
return this.sessionAllowed.has(toolName) || this.alwaysAllowed.has(toolName);
|
|
53
|
+
}
|
|
54
|
+
clearSession() {
|
|
55
|
+
this.sessionAllowed.clear();
|
|
56
|
+
}
|
|
57
|
+
clearAll() {
|
|
58
|
+
this.sessionAllowed.clear();
|
|
59
|
+
this.alwaysAllowed.clear();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=permission.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission.js","sourceRoot":"","sources":["../src/permission.ts"],"names":[],"mappings":"AA+BA,MAAM,YAAY,GAAsB,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC;AAEzD,SAAS,iBAAiB,CAAC,QAA4B;IACrD,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,QAAQ,KAAK,KAAK;QAAE,OAAO,MAAM,CAAC;IACtC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,OAAO,iBAAiB;IACX,OAAO,CAAoB;IAC3B,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,SAAS,CAAsC;IAEhE,YACE,OAA2B,EAC3B,SAAmC;QAEnC,IAAI,CAAC,OAAO,GAAG,OAAO,IAAI,YAAY,CAAC;QACvC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,IAAW,EAAE,SAAqB,EAAE;QACxD,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5E,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;QAErC,IAAI,KAAK,KAAK,MAAM;YAAE,OAAO,KAAK,CAAC;QAEnC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;aAAM,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;QACD,4CAA4C;QAE5C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,SAAS,CAAC,QAAgB,EAAE,QAA8B,SAAS;QACjE,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;YACvB,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,UAAU,CAAC,QAAgB;QACzB,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IAED,SAAS,CAAC,QAAgB;QACxB,OAAO,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC/E,CAAC;IAED,YAAY;QACV,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,QAAQ;QACN,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAC5B,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;IAC7B,CAAC;CACF"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { MessageManager } from './message-manager.js';
|
|
2
|
+
/** Metadata stored alongside the conversation messages */
|
|
3
|
+
export interface SessionMeta {
|
|
4
|
+
readonly sessionId: string;
|
|
5
|
+
readonly createdAt: string;
|
|
6
|
+
readonly updatedAt: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Persists and restores agent conversations to/from disk.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* const sm = new SessionManager('/path/to/sessions');
|
|
13
|
+
* await sm.save(sessionId, messageManager);
|
|
14
|
+
* await sm.load(sessionId, messageManager);
|
|
15
|
+
*/
|
|
16
|
+
export declare class SessionManager {
|
|
17
|
+
private readonly sessionsDir;
|
|
18
|
+
private readonly logger;
|
|
19
|
+
constructor(sessionsDir: string);
|
|
20
|
+
/** Save the current conversation to disk. */
|
|
21
|
+
save(sessionId: string, manager: MessageManager): Promise<void>;
|
|
22
|
+
/** Restore a conversation from disk into the given MessageManager. */
|
|
23
|
+
load(sessionId: string, manager: MessageManager): Promise<SessionMeta>;
|
|
24
|
+
/** Check if a session file exists. */
|
|
25
|
+
exists(sessionId: string): Promise<boolean>;
|
|
26
|
+
/** List all session IDs in the sessions directory. */
|
|
27
|
+
list(): Promise<SessionMeta[]>;
|
|
28
|
+
private sessionPath;
|
|
29
|
+
private readSessionFile;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=session-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,0DAA0D;AAC1D,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAQD;;;;;;;GAOG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,WAAW,EAAE,MAAM;IAK/B,6CAA6C;IACvC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BrE,sEAAsE;IAChE,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC;IAQ5E,sCAAsC;IAChC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASjD,sDAAsD;IAChD,IAAI,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAoBpC,OAAO,CAAC,WAAW;YAML,eAAe;CAQ9B"}
|