Channel history timestamps in summarization requests were rendered with
new Date().toLocaleString(), using the host process timezone instead of
the configured agents.defaults.userTimezone. Reuse the shared inbound
envelope formatter (formatAgentEnvelope + resolveEnvelopeFormatOptions)
so timestamps match the user timezone, consistent with Mattermost/Feishu.
Adds formatSummarizationHistoryText with coverage for the UTC baseline
and non-UTC offsets.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(tlon): chunk outbound text over the declared delivery limit
* fix(tlon): guard optional outbound adapter in chunking test
* test(tlon): prove chunked outbound over real urbit loopback
parseSettingsResponse and applySettingsUpdate read/wrote a dead
`autoDiscover` key, while every consumer (monitor/settings-helpers.ts)
and the settings migration use `autoDiscoverChannels`. As a result the
settings-store hot-reload override for auto-discover-channels never took
effect and the migration re-fired on every restart. Align the field name
and add a regression test covering both the load and subscription paths.
Co-authored-by: Claude <noreply@anthropic.com>
* fix(tlon): stop reconnecting after SSE client close
* fix(tlon): cancel SSE reconnect timers when monitoring stops
Preserve the original Tlon SSE reconnect fix while canceling both retry timers at stopReceiving(), the real monitor shutdown boundary.
Verified against an authenticated, SSRF-guarded loopback SSE server; both reconnect waits settle immediately without changing replay, close cleanup, or uninterrupted reconnection.
Co-authored-by: wahaha1223 <0668001153@xydigit.com>
* fix(tlon): prepare conflict-free reconnect refresh
Prepare a conflict-free refresh of the original Tlon SSE shutdown correction without changing its ownership or losing the contributor commit.
Co-authored-by: wahaha1223 <0668001153@xydigit.com>
* fix(tlon): cancel reconnect timers when monitoring stops
Cancel both Tlon SSE reconnect waits at stopReceiving(), the monitor-owned shutdown boundary, while preserving guarded HTTP, durable same-channel replay, and the original contributor's fix.
Verified with authenticated loopback SSE, both real retry delays, normal reconnection, all 252 Tlon tests, extension type checks, lint, and focused owner regressions.
Co-authored-by: wahaha1223 <0668001153@xydigit.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* [AI] fix(tlon): use waitUntilAbort to fix monitor abort-race hang
Replace ad-hoc abort listener with the shared waitUntilAbort
primitive, hoist pollInterval for unified finally cleanup, and
add .unref() per codebase convention.
The original code registered an abort listener inside a Promise
constructor without checking signal.aborted first. Per the
AbortSignal contract, a listener attached after abort has fired
is never called, so the monitor hung without running cleanup.
The fix reuses waitUntilAbort from plugin-sdk/channel-outbound,
which correctly handles already-aborted, normal, and absent
signals. pollInterval is hoisted and cleared in finally as a
single cleanup owner.
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes#114886
* fix(tlon): prove abort-safe Urbit monitor cleanup
---------
Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
* build(deps): remove npm shrinkwrap; mirror pnpm lock into transient package locks
npm 12 removed shrinkwrap (command + tarball/root loading). Delete all 82
committed npm-shrinkwrap.json files and stop publishing lockfiles; keep
pnpm-lock.yaml as the single reviewed dependency boundary. The generator
becomes scripts/generate-npm-package-lock.mjs and feeds plugin bundling via
a transient package-lock.json + npm ci (works on npm 11 and 12). Tarball
validation treats the published 2026.7.2 beta train as a shrinkwrap
transition; self-update npm detection now uses install topology instead of
the shipped shrinkwrap.
* fix(deps): repair lint, deadcode, and test-type lanes for the npm 12 migration
- sort integrity comparisons with an explicit comparator (oxlint)
- keep resolveBunGlobalNodeModules module-local (knip unused-export gate)
- model npm pack --json as npm<=11 array / npm 12 name-keyed object
- default calver destructuring in the tarball test fixture
* feat(channels): add channel-owned setup contracts
* test(channels): align legacy setup fixtures
* chore(channels): regenerate config and SDK baselines after rebase
* fix(update): run fresh doctor after current-process core changes
* fix(channels): align add pre-scan with execution precedence
* style(cli): format channels-cli test additions
* fix(channels): restore option-before-positional channel resolution via metadata arity scan
* fix(channels): keep help flags out of metadata arity escalation
* test(update): mock fresh post-update doctor in current-process suites
* style: format review fixes and correct entrypoint mock type
* fix(channels): register only modern contract options for dual-publishing plugins
* test(update): align downgrade suites with fresh-doctor child invocation
* docs(channels): record empty-contract and input-forwarding invariants
* fix(line): keep the shipped --token switch as a channel access token alias
* fix(signal): stop treating exact cross-family loopback endpoints as bind-aligned
* chore(config): regenerate docs config baselines after second rebase
* style: format rebased channels add tests
* fix(channels): enforce field-key and flag-name agreement in setup contracts
* fix(signal): detect container endpoints for bare --http-url setup
* fix(signal): ignore unconfigured accounts in transport collision checks
* fix(channels): validate negated setup flags in contract and normalizer
* fix(signal): preserve existing transport kind when setup detection is unreachable
* style(signal): use direct boolean check in collision guard
* style(signal): type test config literals
* docs(update): record two-read design of fresh-doctor validation gate
* fix(channels): satisfy post-rebase architecture gates
* docs: refresh channel setup map
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Media-facts program PR 5 — the planned additive bridge. Structured
facts travel alongside existing prompt text through prompt prelude,
GetReplyOptions, FollowupRun, queue collect/defer/retry, active
steering, embedded/CLI runs, Gateway offloads (offloadedRefs no longer
discarded; AgentContentPhaseResult carries media), Tlon, and late-media
projection — with prompt bytes golden-equal everywhere. Facts follow
the same adoption/idempotency/late-append lifecycle as prompt text.
Intentionally +175 prod LOC per the audit; the facts-first
hydration/prune deletion PR consumes this carrier next.
* refactor(channels)!: shrink ChannelSetupInput to a generic envelope with a deprecated compatibility tier
* fix(channels): keep ChannelSetupInput structurally assignable without an index signature
* docs: regenerate docs map
* fix(tlon): bound Urbit auth response body drain to prevent OOM
Replace the unbounded response.text() body drain (discarded after reading
to finalize set-cookie headers) with a streaming reader capped at 64 KiB.
When no body stream is available, fall back to text() for compatibility.
A hostile or misconfigured Urbit endpoint could previously stream an
arbitrarily large body into memory during login.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(tlon): add curly braces to drain loop for lint compliance
* fix(tlon): remove unbounded text() fallback, cancel reader in finally
Remove the unbounded response.text() body-less fallback path. When no body
stream is available, there is nothing to drain — cookie headers are already
finalised. Move reader.cancel() into a finally block so the reader is
released even when read() throws.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* refactor(channels): shared supplemental sender gating, allowlist-match adoption, outbound mop-ups
* fix(plugin-sdk): skip-aware media sequence with text fallback for empty URLs
* chore(plugin-sdk): align surface budgets after rebase
* test(qqbot): type media sender mock calls
* fix(plugin-sdk): distinguish empty media sequences
* fix(plugin-sdk): track void media sends
* feat(mattermost): add thread.requireExplicitMention to opt out of thread auto-follow
Mattermost treats any reply in a thread the bot has participated in as an
implicit mention, so requireMention only gates the first message and the bot
then answers follow-ups addressed to other people for the participation TTL.
Slack exposes channels.slack.thread.requireExplicitMention for exactly this;
Mattermost had no equivalent and its strict schema rejected the key.
Add channels.mattermost.thread.requireExplicitMention (channel + per-account),
mirroring Slack. When set, thread participation no longer counts as a mention.
Default (unset/false) keeps today's auto-follow behavior unchanged.
Related: #108269
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(channels): add implicit mention policy foundation
* feat(channels): unify implicit mention policy
Co-authored-by: leon <dodoma0919@gmail.com>
* refactor(channels): keep implicit policy inside evaluator
* fix(channels): use exported implicit mention type
* chore(channels): satisfy extension lint
* fix(config): break implicit mention type cycle
* fix(plugin-sdk): account for implicit mention config export
* refactor(config): isolate implicit mention schema
* chore(plugin-sdk): align implicit mention surface budget
* fix(config): remove unused schema re-export
---------
Co-authored-by: leon <dodoma0919@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* chore(plugins): gate deprecation hygiene in CI and purge internal deprecated usage
* fix(agents): migrate auth-profiles oauth import to OAuthProviderId
* fix(whatsapp): use durability param in delivery-recovery test
* chore(scripts): declare shouldRunDeprecationHygieneChecks in check-changed types
* test(plugins): update runtime-api guard for ssrf policy rename
* fix(tlon): keep fragment image URLs as media
Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
* test(tlon): prove fragment image media send path
Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
* test(tlon): remove mocked fragment outbound proof
Keep the PR evidence scoped to the helper-level regression test until live Tlon proof is available. The mocked outbound test was not sufficient for the reviewer-requested live behavior proof and triggered serialized-state review noise.
Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
* docs: refresh docs map for CI
Regenerate docs/docs_map.md after syncing with upstream/main so the docs map check passes on this PR branch.
Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
* fix(tlon): constrain fragment image URL matching
Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
* fix(tlon): harden image URL classification
Co-authored-by: VectorPeak <garrufariw@gmail.com>
---------
Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
Co-authored-by: VectorPeak <VectorPeak@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>