Connects the inbound SenderID, which was already carried end-to-end via
InboundContext to PromptBuildRequest, into the per-turn user message so
historical messages remember who said what.
- userPromptMessage gains a senderID parameter. Real human IDs are
sanitized via SanitizeMessageName; synthetic triggers (cron,
heartbeat, async:*) are filtered out via IsSystemSenderID so they
do not surface as distinct users.
- pipeline_setup.go now passes ts.opts.Dispatch.SenderID() and uses
AddFullMessage unconditionally. The previous AddMessage(role,
content) shortcut for media-less turns silently dropped the new
Name field, breaking history disambiguation.
- context.go (BuildMessagesFromPrompt) propagates req.SenderID onto
the current-turn user message that goes to the LLM.
After this commit, sessions persist sender attribution and provider
adapters receive it on each user message. Adapters that consume Name
(OpenAI native field, Anthropic-style content prefix) land in
follow-up commits.
The existing "## Current Sender" block in buildDynamicContext is
unchanged; it continues to describe the latest speaker in the system
prompt while Name covers the per-message channel for history.
Refs #2702.
Two more helpers needed for end-to-end multi-user attribution:
- IsSystemSenderID filters synthetic trigger sources (cron, heartbeat,
async:* tool callbacks, system channel) so internal events do not
surface as distinct human users in the conversation. Without this,
cron-triggered messages would carry attribution like cron, producing
a confusing [cron] [System: cron] ... double prefix on adapters that
fall back to in-content rendering.
- ApplyUserNamePrefix renders msg.Name as a [name] prefix on user
content for adapters whose APIs do not support a per-message author
identity (Anthropic, Bedrock, OpenAI Responses). The persisted
message is never mutated; only the wire payload carries the prefix.
Tool result messages and non-user roles pass through unchanged.
These helpers are pure and independently testable; they will be wired
into the agent and provider adapters in subsequent commits.
Refs #2702.
OpenAI's Chat Completions name field requires ^[a-zA-Z0-9_-]{1,64}$.
Real-world sender IDs (Discord alice#1234, numeric Telegram IDs, Slack
U07AB12C3DEF, free-form CLI names) often violate this, so a sanitizer
sits between the raw inbound ID and any wire payload.
Disallowed characters collapse into a single underscore, leading and
trailing underscores are trimmed, and the result is byte-safely
truncated to 64 (output is pure ASCII).
This helper will be wired into the agent pipeline in a later commit.
Refs #2702.
Adds an optional Name field on providers.Message so per-message sender
attribution can be carried through the persistence layer and onto the
wire. The field is JSON-tagged `name,omitempty`, so:
- the OpenAI Chat Completions wire format will be able to emit the
native `name` field that disambiguates participants in multi-user
conversations;
- session stores that round-trip messages via json.Marshal preserve
the field with no extra plumbing;
- existing session files load cleanly with Name = "".
This is the schema-only change. Adapters and the agent pipeline are
updated in subsequent commits.
Refs #2702.
* fix(docker): restore `make docker-build` by adding build directives and fixing Go version
docker-compose.yml only had `image:` references with no `build:` sections,
so `docker compose build` had nothing to build. Also fixed golang:1.26.0-alpine
(nonexistent) to golang:1.25-alpine in Dockerfile.full/heavy, and removed
LICENSE from .dockerignore since scripts/copydir.go needs it as a repo-root anchor
during `go generate`.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(docker): inject version metadata ldflags in Dockerfile.launcher
Mirror the ldflags from web/Makefile (Version, GitCommit, BuildTime,
GoVersion) into the picoclaw-launcher go build command so Docker-built
launcher images include proper version/build metadata.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(tools): add cross-platform serial hardware tool
* feat(config): wire serial tool into runtime and dashboard
* hardware/serial: tighten validation and error handling
* hardware/serial: improve unix cancellation and timeout polling
* hardware/serial: improve windows I/O handling
* hardware/serial: fix darwin cross-compilation build
* docs(design): summarize hardware support and serial limits
* build: keep go generate on host during cross builds
* onboard: drop unrelated go generate change from serial work
* style(tools): wrap serial lines for golines
* refactor(pico): unify message kind handling of tool_calls and thought
* fix(pico): add legacy compatibility for thought payload in Send method
Co-authored-by: Copilot <copilot@github.com>
---------
Co-authored-by: Copilot <copilot@github.com>
Two-phase strategy: 7 days inactive → stale warning, 7 more days → close.
Exempt labels: pinned, keep-open, wip, do-not-close, type: roadmap.
Draft PRs are also exempt. Runs daily at 03:00 JST.
Scan oldest items first (ascending: true) with 500 ops budget to avoid
backlog starvation.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>