Compare commits

..

1 Commits

Author SHA1 Message Date
Hermes Agent
4bfbac8b7d feat(tmux): agent-send.sh --class triage tag for the comms daemon
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
Add an optional triage class to inter-agent messages so a comms daemon (M8)
can route deliver-vs-log-and-drop from an exact field instead of re-deriving
intent from the message body. ~35% of Mos's queued fan-in is agent-send
traffic; this makes that slice self-declaring on the tmux transport today,
with zero dependency on the M7/Matrix cutover.

Producer:
  -C CLASS / --class CLASS / --class=CLASS, c in
  {terminal-log, actionable, human, reaction}.
  When SET, the preamble carries a ` class=<c>` token INSIDE the bracket:
      [src -> dst class=terminal-log] msg
  When OMITTED, NO token is emitted — the preamble is byte-for-byte identical
  to the classic format (regression bar). Consumers treat an absent class as
  'actionable' (fail-safe: the agent still sees it). Invalid/empty class => exit 3.

Consumer grammar (daemon mirrors this exactly):
  ^\[(\S+) -> (\S+?)(?: class=(terminal-log|actionable|human|reaction))?\] (.*)$

Tests (agent-send.test.sh, 11 assertions, all green; shellcheck clean):
  - REGRESSION: no --class is byte-identical to origin/main (proven via od -tx1
    diff of the on-wire payload, not just an expected string).
  - space / equals / -C short forms all parse identically.
  - invalid class and valueless --class both exit 3 with nothing sent.
  - the documented consumer regex round-trips every class + the classic line.

SENDER is now env-overridable (AGENT_SEND_SENDER) purely for test injection;
production callers never set it, so behavior is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kt2D8TsnDwhtzEAPijsNmR
2026-06-22 22:16:13 -05:00
5 changed files with 5 additions and 30 deletions

7
.gitignore vendored
View File

@@ -15,10 +15,3 @@ infra/step-ca/dev-password
# Scratch dirs created by the framework git-wrapper shell test harnesses # Scratch dirs created by the framework git-wrapper shell test harnesses
.mosaic-test-work/ .mosaic-test-work/
# Transient config files vite/vitest/esbuild write next to a *.config.ts while
# loading it, then unlink. They are untracked but were not ignored, so turbo's
# package traversal hashed them and intermittently failed CI with "Package
# traversal error: ... .timestamp-*.mjs: No such file or directory" when the
# file vanished mid-scan. Ignoring them removes the race.
*.timestamp-*.mjs

View File

@@ -28,7 +28,6 @@ export default tseslint.config(
'apps/web/e2e/helpers/*.ts', 'apps/web/e2e/helpers/*.ts',
'apps/web/playwright.config.ts', 'apps/web/playwright.config.ts',
'apps/gateway/vitest.config.ts', 'apps/gateway/vitest.config.ts',
'packages/db/vitest.config.ts',
'packages/storage/vitest.config.ts', 'packages/storage/vitest.config.ts',
'packages/mosaic/__tests__/*.ts', 'packages/mosaic/__tests__/*.ts',
'tools/federation-harness/*.ts', 'tools/federation-harness/*.ts',

View File

@@ -4,22 +4,5 @@ export default defineConfig({
test: { test: {
globals: true, globals: true,
environment: 'node', environment: 'node',
// The migration suite spins up a real PGlite (WASM Postgres) instance per
// test and applies the full drizzle migration set. Each case legitimately
// takes ~5s locally and considerably longer on CI, where turbo runs many
// packages' test suites concurrently. The 5s vitest default then expires
// mid-migration and the run fails as a phantom "Test timed out in 5000ms"
// (often surfacing the underlying WASM `memory access out of bounds` when
// the heap is starved). Give migrations real headroom.
testTimeout: 120_000,
hookTimeout: 120_000,
// Each PGlite instance carries a multi-hundred-MB WASM heap. Running test
// files in parallel forks multiplies that peak and is what tips the CI
// runner into the WASM OOM. A single fork keeps only one instance resident
// at a time — slightly slower, but deterministic.
pool: 'forks',
poolOptions: {
forks: { singleFork: true },
},
}, },
}); });

View File

@@ -128,8 +128,8 @@ PY
merge_gitea_with_api() { merge_gitea_with_api() {
local host="$1" api_url token basic_auth body_file raw_code payload local host="$1" api_url token basic_auth body_file raw_code payload
api_url="https://${host}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_NUMBER}/merge" api_url="https://${host}/api/v1/repos/${OWNER}/${REPO}/pulls/${PR_NUMBER}/merge"
mkdir -p "${AGENT_WORK_ROOT:-${HOME:-/tmp}/mosaic/agent-work}" mkdir -p "${AGENT_WORK_ROOT:-/home/hermes/agent-work}"
body_file=$(mktemp "${AGENT_WORK_ROOT:-${HOME:-/tmp}/mosaic/agent-work}/pr-merge-api-response.XXXXXX") body_file=$(mktemp "${AGENT_WORK_ROOT:-/home/hermes/agent-work}/pr-merge-api-response.XXXXXX")
payload='{"Do":"squash"}' payload='{"Do":"squash"}'
token=$(get_gitea_token "$host" || true) token=$(get_gitea_token "$host" || true)
@@ -214,8 +214,8 @@ case "$PLATFORM" in
TEA_LOGIN="$(get_gitea_login_for_host "$HOST" || true)" TEA_LOGIN="$(get_gitea_login_for_host "$HOST" || true)"
if [[ -n "$TEA_LOGIN" ]]; then if [[ -n "$TEA_LOGIN" ]]; then
mkdir -p "${AGENT_WORK_ROOT:-${HOME:-/tmp}/mosaic/agent-work}" mkdir -p "${AGENT_WORK_ROOT:-/home/hermes/agent-work}"
TEA_ERROR_FILE=$(mktemp "${AGENT_WORK_ROOT:-${HOME:-/tmp}/mosaic/agent-work}/pr-merge-tea-error.XXXXXX") TEA_ERROR_FILE=$(mktemp "${AGENT_WORK_ROOT:-/home/hermes/agent-work}/pr-merge-tea-error.XXXXXX")
if tea pr merge "$PR_NUMBER" --style squash --repo "$OWNER/$REPO" --login "$TEA_LOGIN" 2> "$TEA_ERROR_FILE"; then if tea pr merge "$PR_NUMBER" --style squash --repo "$OWNER/$REPO" --login "$TEA_LOGIN" 2> "$TEA_ERROR_FILE"; then
rm -f "$TEA_ERROR_FILE" rm -f "$TEA_ERROR_FILE"
elif is_known_tea_empty_identity_failure "$TEA_ERROR_FILE"; then elif is_known_tea_empty_identity_failure "$TEA_ERROR_FILE"; then

View File

@@ -4,7 +4,7 @@
set -euo pipefail set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORK_ROOT="${AGENT_WORK_ROOT:-${HOME:-/tmp}/mosaic/agent-work}" WORK_ROOT="${AGENT_WORK_ROOT:-/home/hermes/agent-work}"
SANDBOX="$WORK_ROOT/pr-merge-empty-uid-test-$$" SANDBOX="$WORK_ROOT/pr-merge-empty-uid-test-$$"
MOCK_BIN="$SANDBOX/bin" MOCK_BIN="$SANDBOX/bin"
REPO_DIR="$SANDBOX/repo" REPO_DIR="$SANDBOX/repo"