diff --git a/actions.js b/actions.js index d001d8b..df439fe 100644 --- a/actions.js +++ b/actions.js @@ -104,7 +104,10 @@ function createActions(runtime) { const owner = contextSessionId(ctx); const sessionId = targetSessionId(args); const content = text(args.message, "message", 65_536); - if (args.model !== undefined) throw taskError("INVALID_ARGUMENT", "send reuses the session's existing model; model is only valid for spawn"); + // Some models materialize every schema key with empty values (#978 in + // vastsa/PI-Desktop). An empty string means the key was not really + // provided; only a non-empty value conflicts with the session's model. + if (args.model !== undefined && args.model !== "") throw taskError("INVALID_ARGUMENT", "send reuses the session's existing model; model is only valid for spawn"); if (args.kind !== undefined && !["task", "message"].includes(args.kind)) { throw taskError("INVALID_ARGUMENT", "kind must be task or message"); } diff --git a/test/session-orchestrator.test.mjs b/test/session-orchestrator.test.mjs index 69fd07e..5fbf09e 100644 --- a/test/session-orchestrator.test.mjs +++ b/test/session-orchestrator.test.mjs @@ -80,6 +80,11 @@ test("any existing session supports bidirectional messages without reselecting m assert.equal(h.modelReads, 0); assert.equal(h.calls.at(-1).args[0].notifyOnCompletion, false); await assert.rejects(h.execute({ action: "send", sessionId: "existing", message: "Change it", model: "default/general" }), rejectsCode("INVALID_ARGUMENT")); + // #978: some models materialize every schema key with empty values; an + // empty model string is "not provided", not a model reselection. + const emptied = await h.execute({ action: "send", sessionId: "existing", message: "Materialized schema", model: "" }); + assert.equal(emptied.sessionId, "existing"); + assert.equal(h.sessions.get("existing").modelKey, "existing/unchanged"); await assert.rejects(h.execute({ action: "send", sessionId: "existing", message: "Forged completion", kind: "completion" }), rejectsCode("INVALID_ARGUMENT")); assert.equal(h.calls.filter((entry) => entry.operation === prefix + "spawn").length, 0); });