@selesai/code 0.3.10 → 0.5.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/core/agent-session-auto-handoff.test.d.ts +2 -0
- package/dist/core/agent-session-auto-handoff.test.d.ts.map +1 -0
- package/dist/core/agent-session-auto-handoff.test.js +161 -0
- package/dist/core/agent-session-auto-handoff.test.js.map +1 -0
- package/dist/core/agent-session.d.ts +4 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +43 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/settings-manager-auto-handoff.test.d.ts +2 -0
- package/dist/core/settings-manager-auto-handoff.test.d.ts.map +1 -0
- package/dist/core/settings-manager-auto-handoff.test.js +29 -0
- package/dist/core/settings-manager-auto-handoff.test.js.map +1 -0
- package/dist/core/settings-manager.d.ts +9 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +22 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/defaults/models.json +2 -3
- package/dist/extensions/caveman/index.js +16 -1
- package/dist/extensions/caveman/test/extension.test.js +7 -4
- package/dist/extensions/caveman/test/helpers.test.js +12 -1
- package/dist/extensions/context-compaction-reminder.test.ts +82 -0
- package/dist/extensions/context-compaction-reminder.ts +28 -0
- package/dist/extensions/handoff-new.test.ts +2 -13
- package/dist/extensions/handoff-new.ts +16 -25
- package/dist/extensions/package.json +1 -0
- package/dist/extensions/pi-powerline-footer/index.ts +43 -2
- package/dist/extensions/pi-powerline-footer/session-usage.ts +44 -0
- package/dist/extensions/pi-powerline-footer/tests/session-usage.test.ts +47 -0
- package/dist/extensions/pi-subagents/README.md +1 -1
- package/dist/extensions/pi-subagents/agents/architect.md +9 -29
- package/dist/extensions/pi-subagents/agents/builder.md +17 -106
- package/dist/extensions/pi-subagents/agents/commentator.md +20 -117
- package/dist/extensions/pi-subagents/agents/explorer.md +14 -35
- package/dist/extensions/pi-subagents/agents/recapper.md +20 -12
- package/dist/extensions/pi-subagents/agents/researcher.md +11 -34
- package/dist/extensions/pi-subagents/src/agents/agents.ts +46 -0
- package/dist/extensions/pi-subagents/src/extension/index.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +24 -0
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +25 -1
- package/dist/extensions/question/constants.ts +0 -7
- package/dist/extensions/question/index.ts +11 -108
- package/dist/extensions/question/schemas.ts +1 -26
- package/dist/extensions/question/shortcuts.ts +5 -14
- package/dist/extensions/question/tests/question-list.test.ts +8 -0
- package/dist/extensions/question/tests/ui-protocol.test.ts +1 -7
- package/dist/extensions/question/tui-adapter.ts +3 -20
- package/dist/extensions/question/types.ts +0 -6
- package/dist/extensions/question/ui-protocol.ts +2 -17
- package/dist/extensions/workflow/adapter.ts +395 -275
- package/dist/extensions/workflow/extension.ts +2 -1
- package/dist/extensions/workflow/modes/prototype.ts +17 -19
- package/dist/extensions/workflow/modes/quick.ts +17 -19
- package/dist/extensions/workflow/modes/task.ts +82 -0
- package/dist/extensions/workflow/run-state.ts +124 -0
- package/dist/extensions/workflow/state-machine.ts +7 -8
- package/dist/extensions/workflow/task-validators.ts +69 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +4 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +20 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +8 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +8 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +12 -0
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +14 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +20 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/skills/workflow-creation/SKILL.md +72 -0
- package/docs/workflows.md +46 -7
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-session-auto-handoff.test.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-auto-handoff.test.ts"],"names":[],"mappings":"","sourcesContent":["import { describe, expect, it, vi } from \"vitest\";\nimport { AgentSession } from \"./agent-session.ts\";\nimport type { ExtensionRunner } from \"./extensions/runner.ts\";\nimport type { ResolvedCommand } from \"./extensions/types.ts\";\nimport { SettingsManager } from \"./settings-manager.ts\";\n\nfunction createMockSession({\n\tenabled,\n\tthreshold = 128_000,\n\ttokens,\n\tmode = \"tui\",\n\tcommand,\n}: {\n\tenabled: boolean;\n\tthreshold?: number;\n\ttokens: number | null;\n\tmode?: \"tui\" | \"print\" | \"rpc\";\n\tcommand?: ResolvedCommand | undefined;\n}) {\n\tconst settings = SettingsManager.inMemory({\n\t\tautoHandoff: { enabled, thresholdTokens: threshold },\n\t});\n\n\tconst getCommand = vi.fn(() => command);\n\tconst handler = command?.handler ?? vi.fn();\n\tconst createCommandContext = vi.fn(() => ({ mock: \"ctx\" }) as unknown as never);\n\tconst emitError = vi.fn();\n\n\tconst extensionRunner = {\n\t\tgetCommand,\n\t\tcreateCommandContext,\n\t\temitError,\n\t\temit: vi.fn(),\n\t} as unknown as ExtensionRunner;\n\n\tconst resourceLoader = {\n\t\tgetExtensions: () => ({ extensions: [], errors: [], runtime: { flagValues: new Map(), pendingProviderRegistrations: [], assertActive: () => {} } }),\n\t\tgetSkills: () => ({ skills: [], diagnostics: [] }),\n\t\tgetPrompts: () => ({ prompts: [], diagnostics: [] }),\n\t\tgetThemes: () => ({ themes: [], diagnostics: [] }),\n\t\tgetAgents: () => ({ agents: [], diagnostics: [] }),\n\t\tgetAgentsFiles: () => ({ agentsFiles: [] }),\n\t\tgetSystemPrompt: () => undefined,\n\t\tgetAppendSystemPrompt: () => [],\n\t\tgetExtensionDiagnostics: () => [],\n\t\textendResources: () => {},\n\t\treload: vi.fn(),\n\t};\n\n\tconst modelRegistry = {\n\t\tregisterProvider: vi.fn(),\n\t\tunregisterProvider: vi.fn(),\n\t\tgetProvider: vi.fn(),\n\t\tgetProviders: vi.fn(() => []),\n\t\tgetModels: vi.fn(() => []),\n\t};\n\n\tconst agent = {\n\t\tstate: { messages: [], tools: [] },\n\t\tsubscribe: vi.fn(() => () => {}),\n\t};\n\n\tconst session = new AgentSession({\n\t\tagent: agent as unknown as never,\n\t\tsessionManager: { getBranch: () => [] } as unknown as never,\n\t\tsettingsManager: settings,\n\t\tcwd: \"/tmp\",\n\t\tresourceLoader: resourceLoader as unknown as never,\n\t\tmodelRegistry: modelRegistry as unknown as never,\n\t});\n\n\t(session as any)._extensionRunner = extensionRunner;\n\t(session as any)._extensionMode = mode;\n\t(session as any).getContextUsage = () =>\n\t\ttokens === null ? null : { tokens, contextWindow: 200_000, percent: tokens / 200_000 };\n\n\treturn { session, getCommand, createCommandContext, handler, emitError, settings };\n}\n\ndescribe(\"AgentSession auto handoff\", () => {\n\tit(\"invokes handoff-new once when enabled and threshold reached in tui mode\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand, createCommandContext, emitError } = createMockSession({\n\t\t\tenabled: true,\n\t\t\ttokens: 128_000,\n\t\t\tcommand,\n\t\t});\n\n\t\tawait (session as any)._checkAutoHandoff();\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).toHaveBeenCalledWith(\"handoff-new\");\n\t\texpect(createCommandContext).toHaveBeenCalled();\n\t\texpect(command.handler).toHaveBeenCalledTimes(1);\n\t\texpect(command.handler).toHaveBeenCalledWith(\"\", expect.anything());\n\t\texpect(emitError).not.toHaveBeenCalled();\n\t});\n\n\tit(\"dispatches auto handoff via _emitAgentSettled in tui mode\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session } = createMockSession({ enabled: true, tokens: 200_000, command });\n\t\tconst events: string[] = [];\n\t\tsession.subscribe((event) => {\n\t\t\tif (event.type === \"agent_settled\") events.push(event.type);\n\t\t});\n\n\t\tawait (session as any)._emitAgentSettled();\n\n\t\texpect(events).toEqual([\"agent_settled\"]);\n\t\texpect(command.handler).toHaveBeenCalledTimes(1);\n\t});\n\n\tit(\"_emitAgentSettled does not auto-handoff in non-tui mode\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session } = createMockSession({\n\t\t\tenabled: true,\n\t\t\ttokens: 200_000,\n\t\t\tmode: \"print\",\n\t\t\tcommand,\n\t\t});\n\t\tconst events: string[] = [];\n\t\tsession.subscribe((event) => {\n\t\t\tif (event.type === \"agent_settled\") events.push(event.type);\n\t\t});\n\n\t\tawait (session as any)._emitAgentSettled();\n\n\t\texpect(events).toEqual([\"agent_settled\"]);\n\t\texpect(command.handler).not.toHaveBeenCalled();\n\t});\n\n\tit(\"does nothing when disabled\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand } = createMockSession({ enabled: false, tokens: 200_000, command });\n\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).not.toHaveBeenCalled();\n\t});\n\n\tit(\"does nothing when tokens are below threshold\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand } = createMockSession({ enabled: true, tokens: 127_999, command });\n\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).not.toHaveBeenCalled();\n\t});\n\n\tit(\"does nothing when tokens are null\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand } = createMockSession({ enabled: true, tokens: null, command });\n\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).not.toHaveBeenCalled();\n\t});\n\n\tit(\"does nothing in non-tui mode\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand } = createMockSession({\n\t\t\tenabled: true,\n\t\t\ttokens: 200_000,\n\t\t\tmode: \"rpc\",\n\t\t\tcommand,\n\t\t});\n\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).not.toHaveBeenCalled();\n\t});\n\n\tit(\"does nothing when handoff-new command is not registered\", async () => {\n\t\tconst { session, getCommand } = createMockSession({ enabled: true, tokens: 200_000, command: undefined });\n\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).toHaveBeenCalledWith(\"handoff-new\");\n\t});\n\n\tit(\"emits error when handoff-new throws but does not rethrow\", async () => {\n\t\tconst command = {\n\t\t\thandler: vi.fn(() => Promise.reject(new Error(\"boom\"))),\n\t\t} as unknown as ResolvedCommand;\n\t\tconst { session, emitError } = createMockSession({ enabled: true, tokens: 200_000, command });\n\n\t\tawait expect((session as any)._checkAutoHandoff()).resolves.toBeUndefined();\n\n\t\texpect(emitError).toHaveBeenCalledWith({\n\t\t\textensionPath: \"command:handoff-new\",\n\t\t\tevent: \"auto-handoff\",\n\t\t\terror: \"boom\",\n\t\t});\n\t});\n\n\tit(\"resets triggered flag when tokens drop below threshold\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand } = createMockSession({ enabled: true, tokens: 200_000, command });\n\n\t\tawait (session as any)._checkAutoHandoff();\n\t\texpect(command.handler).toHaveBeenCalledTimes(1);\n\n\t\t// Simulate tokens dropping below threshold (e.g. compaction/new session)\n\t\t(session as any).getContextUsage = () => ({ tokens: 1000, contextWindow: 200_000, percent: 0.5 });\n\t\tawait (session as any)._checkAutoHandoff();\n\t\texpect(command.handler).toHaveBeenCalledTimes(1); // still once\n\n\t\t// Tokens rise above threshold again\n\t\t(session as any).getContextUsage = () => ({ tokens: 200_000, contextWindow: 200_000, percent: 100 });\n\t\tawait (session as any)._checkAutoHandoff();\n\t\texpect(command.handler).toHaveBeenCalledTimes(2);\n\t});\n});\n"]}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { AgentSession } from "./agent-session.js";
|
|
3
|
+
import { SettingsManager } from "./settings-manager.js";
|
|
4
|
+
function createMockSession({ enabled, threshold = 128_000, tokens, mode = "tui", command, }) {
|
|
5
|
+
const settings = SettingsManager.inMemory({
|
|
6
|
+
autoHandoff: { enabled, thresholdTokens: threshold },
|
|
7
|
+
});
|
|
8
|
+
const getCommand = vi.fn(() => command);
|
|
9
|
+
const handler = command?.handler ?? vi.fn();
|
|
10
|
+
const createCommandContext = vi.fn(() => ({ mock: "ctx" }));
|
|
11
|
+
const emitError = vi.fn();
|
|
12
|
+
const extensionRunner = {
|
|
13
|
+
getCommand,
|
|
14
|
+
createCommandContext,
|
|
15
|
+
emitError,
|
|
16
|
+
emit: vi.fn(),
|
|
17
|
+
};
|
|
18
|
+
const resourceLoader = {
|
|
19
|
+
getExtensions: () => ({ extensions: [], errors: [], runtime: { flagValues: new Map(), pendingProviderRegistrations: [], assertActive: () => { } } }),
|
|
20
|
+
getSkills: () => ({ skills: [], diagnostics: [] }),
|
|
21
|
+
getPrompts: () => ({ prompts: [], diagnostics: [] }),
|
|
22
|
+
getThemes: () => ({ themes: [], diagnostics: [] }),
|
|
23
|
+
getAgents: () => ({ agents: [], diagnostics: [] }),
|
|
24
|
+
getAgentsFiles: () => ({ agentsFiles: [] }),
|
|
25
|
+
getSystemPrompt: () => undefined,
|
|
26
|
+
getAppendSystemPrompt: () => [],
|
|
27
|
+
getExtensionDiagnostics: () => [],
|
|
28
|
+
extendResources: () => { },
|
|
29
|
+
reload: vi.fn(),
|
|
30
|
+
};
|
|
31
|
+
const modelRegistry = {
|
|
32
|
+
registerProvider: vi.fn(),
|
|
33
|
+
unregisterProvider: vi.fn(),
|
|
34
|
+
getProvider: vi.fn(),
|
|
35
|
+
getProviders: vi.fn(() => []),
|
|
36
|
+
getModels: vi.fn(() => []),
|
|
37
|
+
};
|
|
38
|
+
const agent = {
|
|
39
|
+
state: { messages: [], tools: [] },
|
|
40
|
+
subscribe: vi.fn(() => () => { }),
|
|
41
|
+
};
|
|
42
|
+
const session = new AgentSession({
|
|
43
|
+
agent: agent,
|
|
44
|
+
sessionManager: { getBranch: () => [] },
|
|
45
|
+
settingsManager: settings,
|
|
46
|
+
cwd: "/tmp",
|
|
47
|
+
resourceLoader: resourceLoader,
|
|
48
|
+
modelRegistry: modelRegistry,
|
|
49
|
+
});
|
|
50
|
+
session._extensionRunner = extensionRunner;
|
|
51
|
+
session._extensionMode = mode;
|
|
52
|
+
session.getContextUsage = () => tokens === null ? null : { tokens, contextWindow: 200_000, percent: tokens / 200_000 };
|
|
53
|
+
return { session, getCommand, createCommandContext, handler, emitError, settings };
|
|
54
|
+
}
|
|
55
|
+
describe("AgentSession auto handoff", () => {
|
|
56
|
+
it("invokes handoff-new once when enabled and threshold reached in tui mode", async () => {
|
|
57
|
+
const command = { handler: vi.fn() };
|
|
58
|
+
const { session, getCommand, createCommandContext, emitError } = createMockSession({
|
|
59
|
+
enabled: true,
|
|
60
|
+
tokens: 128_000,
|
|
61
|
+
command,
|
|
62
|
+
});
|
|
63
|
+
await session._checkAutoHandoff();
|
|
64
|
+
await session._checkAutoHandoff();
|
|
65
|
+
expect(getCommand).toHaveBeenCalledWith("handoff-new");
|
|
66
|
+
expect(createCommandContext).toHaveBeenCalled();
|
|
67
|
+
expect(command.handler).toHaveBeenCalledTimes(1);
|
|
68
|
+
expect(command.handler).toHaveBeenCalledWith("", expect.anything());
|
|
69
|
+
expect(emitError).not.toHaveBeenCalled();
|
|
70
|
+
});
|
|
71
|
+
it("dispatches auto handoff via _emitAgentSettled in tui mode", async () => {
|
|
72
|
+
const command = { handler: vi.fn() };
|
|
73
|
+
const { session } = createMockSession({ enabled: true, tokens: 200_000, command });
|
|
74
|
+
const events = [];
|
|
75
|
+
session.subscribe((event) => {
|
|
76
|
+
if (event.type === "agent_settled")
|
|
77
|
+
events.push(event.type);
|
|
78
|
+
});
|
|
79
|
+
await session._emitAgentSettled();
|
|
80
|
+
expect(events).toEqual(["agent_settled"]);
|
|
81
|
+
expect(command.handler).toHaveBeenCalledTimes(1);
|
|
82
|
+
});
|
|
83
|
+
it("_emitAgentSettled does not auto-handoff in non-tui mode", async () => {
|
|
84
|
+
const command = { handler: vi.fn() };
|
|
85
|
+
const { session } = createMockSession({
|
|
86
|
+
enabled: true,
|
|
87
|
+
tokens: 200_000,
|
|
88
|
+
mode: "print",
|
|
89
|
+
command,
|
|
90
|
+
});
|
|
91
|
+
const events = [];
|
|
92
|
+
session.subscribe((event) => {
|
|
93
|
+
if (event.type === "agent_settled")
|
|
94
|
+
events.push(event.type);
|
|
95
|
+
});
|
|
96
|
+
await session._emitAgentSettled();
|
|
97
|
+
expect(events).toEqual(["agent_settled"]);
|
|
98
|
+
expect(command.handler).not.toHaveBeenCalled();
|
|
99
|
+
});
|
|
100
|
+
it("does nothing when disabled", async () => {
|
|
101
|
+
const command = { handler: vi.fn() };
|
|
102
|
+
const { session, getCommand } = createMockSession({ enabled: false, tokens: 200_000, command });
|
|
103
|
+
await session._checkAutoHandoff();
|
|
104
|
+
expect(getCommand).not.toHaveBeenCalled();
|
|
105
|
+
});
|
|
106
|
+
it("does nothing when tokens are below threshold", async () => {
|
|
107
|
+
const command = { handler: vi.fn() };
|
|
108
|
+
const { session, getCommand } = createMockSession({ enabled: true, tokens: 127_999, command });
|
|
109
|
+
await session._checkAutoHandoff();
|
|
110
|
+
expect(getCommand).not.toHaveBeenCalled();
|
|
111
|
+
});
|
|
112
|
+
it("does nothing when tokens are null", async () => {
|
|
113
|
+
const command = { handler: vi.fn() };
|
|
114
|
+
const { session, getCommand } = createMockSession({ enabled: true, tokens: null, command });
|
|
115
|
+
await session._checkAutoHandoff();
|
|
116
|
+
expect(getCommand).not.toHaveBeenCalled();
|
|
117
|
+
});
|
|
118
|
+
it("does nothing in non-tui mode", async () => {
|
|
119
|
+
const command = { handler: vi.fn() };
|
|
120
|
+
const { session, getCommand } = createMockSession({
|
|
121
|
+
enabled: true,
|
|
122
|
+
tokens: 200_000,
|
|
123
|
+
mode: "rpc",
|
|
124
|
+
command,
|
|
125
|
+
});
|
|
126
|
+
await session._checkAutoHandoff();
|
|
127
|
+
expect(getCommand).not.toHaveBeenCalled();
|
|
128
|
+
});
|
|
129
|
+
it("does nothing when handoff-new command is not registered", async () => {
|
|
130
|
+
const { session, getCommand } = createMockSession({ enabled: true, tokens: 200_000, command: undefined });
|
|
131
|
+
await session._checkAutoHandoff();
|
|
132
|
+
expect(getCommand).toHaveBeenCalledWith("handoff-new");
|
|
133
|
+
});
|
|
134
|
+
it("emits error when handoff-new throws but does not rethrow", async () => {
|
|
135
|
+
const command = {
|
|
136
|
+
handler: vi.fn(() => Promise.reject(new Error("boom"))),
|
|
137
|
+
};
|
|
138
|
+
const { session, emitError } = createMockSession({ enabled: true, tokens: 200_000, command });
|
|
139
|
+
await expect(session._checkAutoHandoff()).resolves.toBeUndefined();
|
|
140
|
+
expect(emitError).toHaveBeenCalledWith({
|
|
141
|
+
extensionPath: "command:handoff-new",
|
|
142
|
+
event: "auto-handoff",
|
|
143
|
+
error: "boom",
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
it("resets triggered flag when tokens drop below threshold", async () => {
|
|
147
|
+
const command = { handler: vi.fn() };
|
|
148
|
+
const { session, getCommand } = createMockSession({ enabled: true, tokens: 200_000, command });
|
|
149
|
+
await session._checkAutoHandoff();
|
|
150
|
+
expect(command.handler).toHaveBeenCalledTimes(1);
|
|
151
|
+
// Simulate tokens dropping below threshold (e.g. compaction/new session)
|
|
152
|
+
session.getContextUsage = () => ({ tokens: 1000, contextWindow: 200_000, percent: 0.5 });
|
|
153
|
+
await session._checkAutoHandoff();
|
|
154
|
+
expect(command.handler).toHaveBeenCalledTimes(1); // still once
|
|
155
|
+
// Tokens rise above threshold again
|
|
156
|
+
session.getContextUsage = () => ({ tokens: 200_000, contextWindow: 200_000, percent: 100 });
|
|
157
|
+
await session._checkAutoHandoff();
|
|
158
|
+
expect(command.handler).toHaveBeenCalledTimes(2);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
//# sourceMappingURL=agent-session-auto-handoff.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-session-auto-handoff.test.js","sourceRoot":"","sources":["../../src/core/agent-session-auto-handoff.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, it, vi } from \"vitest\";\nimport { AgentSession } from \"./agent-session.ts\";\nimport type { ExtensionRunner } from \"./extensions/runner.ts\";\nimport type { ResolvedCommand } from \"./extensions/types.ts\";\nimport { SettingsManager } from \"./settings-manager.ts\";\n\nfunction createMockSession({\n\tenabled,\n\tthreshold = 128_000,\n\ttokens,\n\tmode = \"tui\",\n\tcommand,\n}: {\n\tenabled: boolean;\n\tthreshold?: number;\n\ttokens: number | null;\n\tmode?: \"tui\" | \"print\" | \"rpc\";\n\tcommand?: ResolvedCommand | undefined;\n}) {\n\tconst settings = SettingsManager.inMemory({\n\t\tautoHandoff: { enabled, thresholdTokens: threshold },\n\t});\n\n\tconst getCommand = vi.fn(() => command);\n\tconst handler = command?.handler ?? vi.fn();\n\tconst createCommandContext = vi.fn(() => ({ mock: \"ctx\" }) as unknown as never);\n\tconst emitError = vi.fn();\n\n\tconst extensionRunner = {\n\t\tgetCommand,\n\t\tcreateCommandContext,\n\t\temitError,\n\t\temit: vi.fn(),\n\t} as unknown as ExtensionRunner;\n\n\tconst resourceLoader = {\n\t\tgetExtensions: () => ({ extensions: [], errors: [], runtime: { flagValues: new Map(), pendingProviderRegistrations: [], assertActive: () => {} } }),\n\t\tgetSkills: () => ({ skills: [], diagnostics: [] }),\n\t\tgetPrompts: () => ({ prompts: [], diagnostics: [] }),\n\t\tgetThemes: () => ({ themes: [], diagnostics: [] }),\n\t\tgetAgents: () => ({ agents: [], diagnostics: [] }),\n\t\tgetAgentsFiles: () => ({ agentsFiles: [] }),\n\t\tgetSystemPrompt: () => undefined,\n\t\tgetAppendSystemPrompt: () => [],\n\t\tgetExtensionDiagnostics: () => [],\n\t\textendResources: () => {},\n\t\treload: vi.fn(),\n\t};\n\n\tconst modelRegistry = {\n\t\tregisterProvider: vi.fn(),\n\t\tunregisterProvider: vi.fn(),\n\t\tgetProvider: vi.fn(),\n\t\tgetProviders: vi.fn(() => []),\n\t\tgetModels: vi.fn(() => []),\n\t};\n\n\tconst agent = {\n\t\tstate: { messages: [], tools: [] },\n\t\tsubscribe: vi.fn(() => () => {}),\n\t};\n\n\tconst session = new AgentSession({\n\t\tagent: agent as unknown as never,\n\t\tsessionManager: { getBranch: () => [] } as unknown as never,\n\t\tsettingsManager: settings,\n\t\tcwd: \"/tmp\",\n\t\tresourceLoader: resourceLoader as unknown as never,\n\t\tmodelRegistry: modelRegistry as unknown as never,\n\t});\n\n\t(session as any)._extensionRunner = extensionRunner;\n\t(session as any)._extensionMode = mode;\n\t(session as any).getContextUsage = () =>\n\t\ttokens === null ? null : { tokens, contextWindow: 200_000, percent: tokens / 200_000 };\n\n\treturn { session, getCommand, createCommandContext, handler, emitError, settings };\n}\n\ndescribe(\"AgentSession auto handoff\", () => {\n\tit(\"invokes handoff-new once when enabled and threshold reached in tui mode\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand, createCommandContext, emitError } = createMockSession({\n\t\t\tenabled: true,\n\t\t\ttokens: 128_000,\n\t\t\tcommand,\n\t\t});\n\n\t\tawait (session as any)._checkAutoHandoff();\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).toHaveBeenCalledWith(\"handoff-new\");\n\t\texpect(createCommandContext).toHaveBeenCalled();\n\t\texpect(command.handler).toHaveBeenCalledTimes(1);\n\t\texpect(command.handler).toHaveBeenCalledWith(\"\", expect.anything());\n\t\texpect(emitError).not.toHaveBeenCalled();\n\t});\n\n\tit(\"dispatches auto handoff via _emitAgentSettled in tui mode\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session } = createMockSession({ enabled: true, tokens: 200_000, command });\n\t\tconst events: string[] = [];\n\t\tsession.subscribe((event) => {\n\t\t\tif (event.type === \"agent_settled\") events.push(event.type);\n\t\t});\n\n\t\tawait (session as any)._emitAgentSettled();\n\n\t\texpect(events).toEqual([\"agent_settled\"]);\n\t\texpect(command.handler).toHaveBeenCalledTimes(1);\n\t});\n\n\tit(\"_emitAgentSettled does not auto-handoff in non-tui mode\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session } = createMockSession({\n\t\t\tenabled: true,\n\t\t\ttokens: 200_000,\n\t\t\tmode: \"print\",\n\t\t\tcommand,\n\t\t});\n\t\tconst events: string[] = [];\n\t\tsession.subscribe((event) => {\n\t\t\tif (event.type === \"agent_settled\") events.push(event.type);\n\t\t});\n\n\t\tawait (session as any)._emitAgentSettled();\n\n\t\texpect(events).toEqual([\"agent_settled\"]);\n\t\texpect(command.handler).not.toHaveBeenCalled();\n\t});\n\n\tit(\"does nothing when disabled\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand } = createMockSession({ enabled: false, tokens: 200_000, command });\n\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).not.toHaveBeenCalled();\n\t});\n\n\tit(\"does nothing when tokens are below threshold\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand } = createMockSession({ enabled: true, tokens: 127_999, command });\n\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).not.toHaveBeenCalled();\n\t});\n\n\tit(\"does nothing when tokens are null\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand } = createMockSession({ enabled: true, tokens: null, command });\n\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).not.toHaveBeenCalled();\n\t});\n\n\tit(\"does nothing in non-tui mode\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand } = createMockSession({\n\t\t\tenabled: true,\n\t\t\ttokens: 200_000,\n\t\t\tmode: \"rpc\",\n\t\t\tcommand,\n\t\t});\n\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).not.toHaveBeenCalled();\n\t});\n\n\tit(\"does nothing when handoff-new command is not registered\", async () => {\n\t\tconst { session, getCommand } = createMockSession({ enabled: true, tokens: 200_000, command: undefined });\n\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).toHaveBeenCalledWith(\"handoff-new\");\n\t});\n\n\tit(\"emits error when handoff-new throws but does not rethrow\", async () => {\n\t\tconst command = {\n\t\t\thandler: vi.fn(() => Promise.reject(new Error(\"boom\"))),\n\t\t} as unknown as ResolvedCommand;\n\t\tconst { session, emitError } = createMockSession({ enabled: true, tokens: 200_000, command });\n\n\t\tawait expect((session as any)._checkAutoHandoff()).resolves.toBeUndefined();\n\n\t\texpect(emitError).toHaveBeenCalledWith({\n\t\t\textensionPath: \"command:handoff-new\",\n\t\t\tevent: \"auto-handoff\",\n\t\t\terror: \"boom\",\n\t\t});\n\t});\n\n\tit(\"resets triggered flag when tokens drop below threshold\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand } = createMockSession({ enabled: true, tokens: 200_000, command });\n\n\t\tawait (session as any)._checkAutoHandoff();\n\t\texpect(command.handler).toHaveBeenCalledTimes(1);\n\n\t\t// Simulate tokens dropping below threshold (e.g. compaction/new session)\n\t\t(session as any).getContextUsage = () => ({ tokens: 1000, contextWindow: 200_000, percent: 0.5 });\n\t\tawait (session as any)._checkAutoHandoff();\n\t\texpect(command.handler).toHaveBeenCalledTimes(1); // still once\n\n\t\t// Tokens rise above threshold again\n\t\t(session as any).getContextUsage = () => ({ tokens: 200_000, contextWindow: 200_000, percent: 100 });\n\t\tawait (session as any)._checkAutoHandoff();\n\t\texpect(command.handler).toHaveBeenCalledTimes(2);\n\t});\n});\n"]}
|
|
@@ -237,6 +237,8 @@ export declare class AgentSession {
|
|
|
237
237
|
private _getIdleWaitPromise;
|
|
238
238
|
private _resolveIdleWaitIfIdle;
|
|
239
239
|
private _emitAgentSettled;
|
|
240
|
+
private _autoHandoffTriggered;
|
|
241
|
+
private _checkAutoHandoff;
|
|
240
242
|
private _lastAssistantMessage;
|
|
241
243
|
/** Internal handler for agent events - shared by subscribe and reconnect */
|
|
242
244
|
private _handleAgentEvent;
|
|
@@ -490,6 +492,8 @@ export declare class AgentSession {
|
|
|
490
492
|
setAutoCompactionEnabled(enabled: boolean): void;
|
|
491
493
|
/** Whether auto-compaction is enabled */
|
|
492
494
|
get autoCompactionEnabled(): boolean;
|
|
495
|
+
get autoHandoffEnabled(): boolean;
|
|
496
|
+
get autoHandoffThresholdTokens(): number;
|
|
493
497
|
bindExtensions(bindings: ExtensionBindings): Promise<void>;
|
|
494
498
|
private extendResourcesFromExtensions;
|
|
495
499
|
private buildExtensionResourcePaths;
|