fix(plugins): resolve handoff issues found in live hosts
This commit is contained in:
@@ -4,4 +4,5 @@
|
||||
|
||||
- An explicit `mem0_handoff` call uses the current DeepSeek session’s derived messages; invoke it outside nested code mode. Uses the [shared handoff logic](../agent-plugin-core/CHANGELOG.md#032).
|
||||
- List and resume shared resources from any supported plugin; the destination is the common local store.
|
||||
- Use native optional-service lookup for image attachments so text-only saves also work in the live harness.
|
||||
- Lightened search prompts: search when prior work may help; repeat only for a specific gap.
|
||||
|
||||
@@ -109,7 +109,6 @@ export function apply(ctx: Context, config: Config): void {
|
||||
if (!session.header.cwd) throw new Error("The native session project directory is unavailable.");
|
||||
if (action === "list" || action === "resume") return await runHandoffAction(new URL("./session_handoff.py", import.meta.url), action, session.header.cwd, resource);
|
||||
if (action !== "save") throw new Error("Handoff action must be save, list, or resume.");
|
||||
const attachments = (ctx as unknown as { attachments?: { readImage(ref: unknown): Promise<{ref: {mediaType: string}; data: Uint8Array}> } }).attachments;
|
||||
// dsh-session-title persists user renames and generated titles as last-wins log events.
|
||||
const titleEvent = [...session.events].reverse().find(event => String(event.type) === "session/title");
|
||||
const nativeTitle = (titleEvent?.data as {title?: unknown} | undefined)?.title;
|
||||
@@ -120,6 +119,8 @@ export function apply(ctx: Context, config: Config): void {
|
||||
}, session.deriveMessages(), {
|
||||
excludeCallId: exec.callId,
|
||||
readImage: async (ref) => {
|
||||
// Optional services must use Cordis lookup; direct access requires inject.
|
||||
const attachments = ctx.get("attachments") as { readImage(ref: unknown): Promise<{ref: {mediaType: string}; data: Uint8Array}> } | undefined;
|
||||
if (!attachments) throw new Error("DeepSeek image attachment storage is unavailable.");
|
||||
const image = await attachments.readImage(ref);
|
||||
return { data: image.data, mediaType: image.ref.mediaType };
|
||||
|
||||
@@ -32,10 +32,12 @@ interface RegisteredTool {
|
||||
|
||||
type HarnessListener = (...args: any[]) => unknown;
|
||||
|
||||
function applyAndCollect(config: Config): Map<string, RegisteredTool> {
|
||||
function applyAndCollect(config: Config, attachments?: unknown): Map<string, RegisteredTool> {
|
||||
const tools = new Map<string, RegisteredTool>();
|
||||
const ctx = {
|
||||
tools: { register: (t: RegisteredTool) => tools.set(t.name, t) },
|
||||
get: (service: string) => service === "attachments" ? attachments : undefined,
|
||||
get attachments() { throw new Error('cannot get property "attachments" without inject'); },
|
||||
on: vi.fn(),
|
||||
};
|
||||
apply(ctx as never, config);
|
||||
@@ -310,6 +312,19 @@ describe("mem0_handoff tool", () => {
|
||||
expect(mockSearch).not.toHaveBeenCalled();
|
||||
expect(mockAdd).not.toHaveBeenCalled();
|
||||
});
|
||||
it("reads native image bytes through Cordis optional service lookup", async () => {
|
||||
const readImage = vi.fn(async () => ({ref: {mediaType: "image/png"}, data: new Uint8Array([104,105])}));
|
||||
const tools = applyAndCollect({apiKey: "k", userId: "u"}, {readImage});
|
||||
const imageExec = {...exec, agent: {session: {...exec.agent.session, deriveMessages: () => [
|
||||
{role: "user", content: [{type: "text", text: "Describe this image"}, {type: "image", attachment: {id: "native-image"}}]},
|
||||
]}}};
|
||||
vi.mocked(runHandoff).mockResolvedValue("Saved image context");
|
||||
expect(await tools.get("mem0_handoff")!.execute({}, imageExec)).toBe("Saved image context");
|
||||
expect(readImage).toHaveBeenCalledWith({id: "native-image"});
|
||||
expect(JSON.stringify(vi.mocked(runHandoff).mock.calls[0][1])).toContain("data:image/png;base64,aGk=");
|
||||
const unavailable = applyAndCollect({apiKey: "k", userId: "u"});
|
||||
expect(await unavailable.get("mem0_handoff")!.execute({}, imageExec)).toContain("attachment storage is unavailable");
|
||||
});
|
||||
it("refuses unfinished sibling tools rather than hiding them with its own invocation", async () => {
|
||||
const tools = applyAndCollect({apiKey: "k", userId: "u"});
|
||||
const siblingExec = {...exec, agent: {session: {...exec.agent.session, deriveMessages: () => [
|
||||
|
||||
Reference in New Issue
Block a user