Adopt the latest Channels/Runtime canaries and add the Open session footer - #16
Draft
jerelvelarde wants to merge 4 commits into
Draft
Adopt the latest Channels/Runtime canaries and add the Open session footer#16jerelvelarde wants to merge 4 commits into
jerelvelarde wants to merge 4 commits into
Conversation
Moves from channels 0.2.2-canary.rc-1 / runtime 1.63.3-canary.rc-1 to 0.4.1-canary.1785477663 / 1.64.2-canary.1785477663 — the newest published pair, and the first that carries managed image delivery in both directions plus tool-progress rendering owned by the SDK rather than the platform. Stable `latest` (channels 0.4.0, runtime 1.64.1) sits behind this canary line on both packages, so pinning stable would roll those changes back.
Two breaking changes in the Channels SDK reach these tests. `IncomingTurn` now requires `operation: MessageOperation` — provider-neutral revision identity (`kind`, `logicalMessageId`, `revisionId`, `mentioned`). A `turnOperation()` helper builds a `created`, channel-addressed revision with a distinct logical id per turn so revisions are never conflated. Every turn now runs on a clone of the agent (`isolateAgentInstance`), and `FakeAgent.clone()` constructs `new FakeAgent(...)` — which discards subclass overrides. The recording double therefore never observed a run, and neither did assertions reading `FakeAgent`'s own counters, since those accumulate on the clone rather than the instance the test holds. Recording now happens outside the instance and `clone()` re-applies this subclass's prototype, so assertions describe what OpenTag sent rather than the post-clone internals of the test double.
Groundwork for remote sessions: a managed reply can carry a link to the same conversation in the Intelligence console, plus the model badge for the turn that produced it. Config is all-or-nothing — a partial console location disables the footer rather than emitting a broken link — and rejects unresolved env templates, non-http schemes, and slugs that would escape the URL path. `pathSegment` selects the console view, so this points at the existing `/threads/:id` today and can move to a dedicated `/sessions/:id` without a code change. `sessionFooter` returns undefined when no canonical thread id is available, which is every turn today: an `IncomingTurn` carries `deliveryId` and `turnId` but no thread id, the delivery transcript response carries none, and the channel-threads route is not runtime-authed. Omitting the link beats guessing at one. Wiring follows once the platform surfaces it. The model badge mirrors the agent's own env resolution, so a test reads agent/agent.py and fails if those defaults drift — otherwise the badge would quietly claim the wrong model answered.
Corrects the premise of the previous commit. The canonical thread id is not
missing from the SDK — the managed delivery adapter sets
`conversationKey: delivery.canonicalThreadId` from the prepared delivery, and
`channelPreparedDeliverySchema` has carried `canonicalThreadId` all along. So
`Thread.conversationKey` already is the canonical thread id on the managed
path, and no platform change is needed to render the footer.
Local adapters put a provider-shaped key in the same field (Slack's
`${teamId}:${channel}:${threadTs}`), which is not a thread id and would link
to a console page for a thread that does not exist. The key is therefore
accepted only when it has canonical UUID shape, and the footer is omitted
otherwise.
A failed footer post is reported and swallowed: the answer has already been
delivered, so a cosmetic addition must never turn a completed turn into a
user-facing failure.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
The OpenTag half of remote sessions — the Claude-Code-style affordance where a Slack reply carries a link that opens the same conversation somewhere with more room.
Draft for review of the approach; the footer works today and needs nothing from the platform.
Changes
chore(deps)— the latest canary pair.channels@0.4.1-canary.1785477663/runtime@1.64.2-canary.1785477663. Stablelatest(0.4.0 / 1.64.1) is behind this canary line on both packages, so pinning stable would roll back managed image delivery and SDK-owned tool rendering.test(channel)— adapt to the 0.4 turn contract. Two breaking changes reach these tests:IncomingTurnnow requiresoperation: MessageOperation— revision identity (kind,logicalMessageId,revisionId,mentioned).isolateAgentInstance), andFakeAgent.clone()buildsnew FakeAgent(...), discarding subclass overrides. The recording double therefore never saw a run, and assertions readingFakeAgent's own counters saw nothing either, since those accumulate on the clone rather than the instance the test holds.Recording now happens outside the instance, and
clone()re-applies the subclass prototype. Assertions describe what OpenTag sent rather than the test double's post-clone internals.feat(session)— the footer.app/session-link.ts+ the wiring inchannel.tsx. After a managed run, the reply carries<…|Open session> · gpt-5.5[low].How the thread id is resolved
Worth stating explicitly, because it is easy to get wrong: the managed delivery adapter sets
conversationKey: delivery.canonicalThreadId, andchannelPreparedDeliverySchemahas always carriedcanonicalThreadId. SoThread.conversationKeyis the canonical thread id on the managed path — no platform change required.Local adapters put a provider-shaped key in the same field (Slack's
${teamId}:${channel}:${threadTs}). That is not a thread id, and linking to it would produce a console URL for a thread that does not exist. The key is therefore accepted only when it has canonical UUID shape, and the footer is omitted otherwise.Other deliberate choices:
pathSegmentselects the console view, so this targets the existing/threads/:idtoday and can move to a dedicated/sessions/:idwith no code change.agent/agent.pyand fails if those defaults drift — otherwise the badge would quietly claim the wrong model answered.Verification
pnpm check-types— cleanpnpm test— 162/162, including an integration assertion that a managed turn's reply contains the footer and a non-managed turn's does notRUNTIME: Connected)Not verified live in Slack. The probe set (image out, image in, tool rows, duplicate delivery, thinking status) never ran: the Kite (Dev) Slack app is not currently delivering into the test project, and the Channels surface went dark on a dev redeploy. So the claim here is "typechecks, tests green, runtime declares" — not "verified end to end."
One upstream nit
FakeAgent.clone()discarding subclasses looks like a bug worth a one-line fix —Object.create(Object.getPrototypeOf(this))or aprotected createClone()hook. SubclassingFakeAgentis the documented way to observe runs, and the SDK's own error message tells you to overrideclone(), which cannot help while the base discards your prototype.Not in this PR
Planning docs (roadmap, PRDs, boundary analysis) are deliberately excluded — they reference private-repo internals and this repo is public. They live in Notion.