diff --git a/apps/server/src/peer/PeerLinkRequests.test.ts b/apps/server/src/peer/PeerLinkRequests.test.ts index 09f285954992b..07bceeb692952 100644 --- a/apps/server/src/peer/PeerLinkRequests.test.ts +++ b/apps/server/src/peer/PeerLinkRequests.test.ts @@ -985,6 +985,11 @@ it.live("naming a machine that is already linked returns the link at once, with // Not both: the address could name another machine than the id. const both = yield* a.call({ environmentId: box.environmentId, url: b.url }); expect(errorOf(both)).toMatchObject({ code: "invalid_request" }); + + // A mention can name the machine the agent runs on, which has nothing to link. + const own = yield* a.call({ environmentId: laptop.environmentId }); + expect(errorOf(own)).toMatchObject({ code: "invalid_request" }); + expect(yield* cardOf(a)).toBeUndefined(); }), ), ); diff --git a/apps/server/src/peer/PeerLinkRequests.ts b/apps/server/src/peer/PeerLinkRequests.ts index 390589a81b4e4..4689b4fbad8e1 100644 --- a/apps/server/src/peer/PeerLinkRequests.ts +++ b/apps/server/src/peer/PeerLinkRequests.ts @@ -198,6 +198,12 @@ const make = Effect.gen(function* () { "Pass either environmentId or url, not both. Pass neither to let the user pick the machine.", }); } + if (input.environmentId === scope.environmentId) { + return yield* new OrchestratorMcpFailure({ + code: "invalid_request", + message: "That is the machine you run on. Work here: it needs no link.", + }); + } // A machine already linked needs no card: the agent can use it now. if (input.environmentId !== undefined) { const linked = yield* usableLink(input.environmentId).pipe( diff --git a/packages/client-runtime/src/composerThreadItems.test.ts b/packages/client-runtime/src/composerThreadItems.test.ts index 564226839abe3..5ac3a081835a1 100644 --- a/packages/client-runtime/src/composerThreadItems.test.ts +++ b/packages/client-runtime/src/composerThreadItems.test.ts @@ -85,7 +85,7 @@ describe("matchComposerEnvironmentItems", () => { { environmentId: EnvironmentId.make("env-mini"), label: "Mac mini", machine: "mac-mini" }, ] as const; - it("offers the other machines matching the query, never the composer's own", () => { + it("offers the machines matching the query, naming the thread's own", () => { expect( matchComposerEnvironmentItems({ environments, @@ -108,7 +108,16 @@ describe("matchComposerEnvironmentItems", () => { environmentId: EnvironmentId.make("env-here"), query: "lap", }), - ).toEqual([]); + ).toEqual([ + { + id: "environment:env-here", + type: "environment", + environmentId: "env-here", + machine: "laptop", + label: "Laptop", + description: "This thread's machine", + }, + ]); expect( matchComposerEnvironmentItems({ environments, diff --git a/packages/client-runtime/src/composerThreadItems.ts b/packages/client-runtime/src/composerThreadItems.ts index 32adee581a6e9..7b065386763a5 100644 --- a/packages/client-runtime/src/composerThreadItems.ts +++ b/packages/client-runtime/src/composerThreadItems.ts @@ -87,9 +87,9 @@ export interface ComposerEnvironmentItem { } /** - * The user's machines the `@` picker offers, so the agent can link or work there. The - * composer's own environment is left out. Like threads, they need a query: bare `@` stays a - * file picker. + * The user's machines the `@` picker offers, so the agent can link or work there. That includes + * the thread's own machine, which the user may still want to name. Like threads, they need a + * query: bare `@` stays a file picker. */ export function matchComposerEnvironmentItems(input: { environments: ReadonlyArray; @@ -99,17 +99,14 @@ export function matchComposerEnvironmentItems(input: { const query = input.query.trim().toLowerCase(); if (query.length === 0) return []; return input.environments - .filter( - (environment) => - environment.environmentId !== input.environmentId && - environment.label.toLowerCase().includes(query), - ) + .filter((environment) => environment.label.toLowerCase().includes(query)) .map((environment) => ({ id: `environment:${environment.environmentId}`, type: "environment", environmentId: environment.environmentId, machine: environment.machine, label: environment.label, - description: "Machine", + description: + environment.environmentId === input.environmentId ? "This thread's machine" : "Machine", })); } diff --git a/packages/shared/src/composerContextReferences.ts b/packages/shared/src/composerContextReferences.ts index a7aeb1373ae10..c7d216cc12224 100644 --- a/packages/shared/src/composerContextReferences.ts +++ b/packages/shared/src/composerContextReferences.ts @@ -353,7 +353,7 @@ function formatComposerContextProviderPayload(record: KnownComposerContextRecord return [ `label: ${record.label}`, `environmentId: ${record.environmentId}`, - "The user means this machine. Not linked here yet: call t3_environment_link with this environmentId. Linked: pass it as environmentId, or target.environmentId for delegate_task.", + "The user means this machine. Not linked here yet: call t3_environment_link with this environmentId, which says so if it is the machine you run on. Linked: pass it as environmentId, or target.environmentId for delegate_task.", ].join("\n"); } }