Agent Lamp in motion

Agent adapter setup

All setup is opt-in. Keep unrelated hooks intact. This repository neither reads nor writes ~/.copilot or ~/.claude. Configuration generation is deterministic for the same paths. Re-running it does not install a duplicate.

Use a durable checkout, run npm ci and npm run build, initialize its own .agent-lamp/config.json with npm run init, and start the bridge with npm start. The generated snippets reference that file and the compiled helper by absolute path so agent working directories do not matter. On macOS, the template records the Node executable's current absolute path; regenerate if an upgrade removes that executable. These shell templates target macOS and Linux, not Windows. Rebuild after editing TypeScript; don't point hooks at .ts source files.

Copilot CLI

  1. Run npm run --silent template:copilot and inspect the output.
  2. Choose one installation scope: a separate agent-lamp.json in your Copilot user hooks directory, or in the target repository's .github/hooks/. Do not put it in both. The default user directory is ~/.copilot/hooks, but COPILOT_HOME can change it.
  3. If an agent-lamp.json already exists, back it up manually and compare before replacing it. Do not overwrite another integration's file. Save only the generated JSON, not terminal commentary.
  4. Restart the CLI; hook discovery happens at startup. Use a harmless task and compare local status with what is actually visible in the agent before relying on it.

The native format uses version: 1, camelCase hook keys, sessionId and numeric millisecond timestamps. It observes sessionStart, userPromptSubmitted, tool lifecycle, notification, agentStop, errorOccurred, and sessionEnd.

agentStop with stopReason: "end_turn" means a response ended. Other stop reasons and explicit child stops are ignored. sessionEnd is termination, not normal turn completion. Recoverable tool/runtime failures count as activity; an unrecoverable error becomes red only if there is no outstanding attention.

The adapter does not register permissionRequest: that hook runs before automatic decisions. Only permission_prompt and elicitation_dialog notifications create terminal attention. agent_idle, background completion and shell notifications do not. Native hook payloads cannot correlate a generic notification to a particular tool or permission resolution. New prompts and post-tool signals therefore leave those alerts latched.

Uninstall by removing only the dedicated agent-lamp.json you created, then restarting the CLI. No other hook files need changing.

Claude Code

  1. Run npm run --silent template:claude and inspect the output.
  2. Choose user settings or a single target repository's Claude settings file. Make your own dated backup before editing. This project never reads credential-bearing settings.
  3. Merge each generated event's entries into the existing hooks arrays. Preserve all existing top-level keys and existing hook entries. Do not replace the whole file or the whole hooks object.
  4. Before appending an entry, look for its exact generated command. If already present under that event, don't add it again. Save valid JSON and restart Claude Code to avoid relying on version-dependent hook reload behavior.

For example, if PreToolUse already contains an unrelated matcher group, keep that group and append the generated Agent Lamp group after it. Generated commands point to integrations/hook.sh and include claude-code, making them identifiable for removal.

PreToolUse for AskUserQuestion records the tool-use ID. Either post-tool outcome clears that same question, not another request. It is an attempted question signal: another pre-tool hook may block the question before it is shown. If no matching post-tool hook arrives, acknowledge the lamp only after checking the host.

Permission and MCP dialog notifications are often delayed about six seconds in terminal sessions, and typing can defer them. idle_prompt is roughly a minute after a response and is ignored, as are authentication-success messages. Don't use notification prose for classification.

Elicitation occurs before other hooks may answer automatically, so it does not itself light an attention color. ElicitationResult supplies an optional elicitation ID and resolves only that ID. The documented generic Notification payload does not provide that correlation. A result or elicitation_response notification must not clear all concurrent elicitation alerts. Generic notification alerts require manual acknowledgement or session termination.

StopFailure reports an API-error turn ending. Stop is not success. SessionEnd on an interactive resume switch marks disconnection rather than pretending the old session was answered. Ordinary SessionStart/resume leaves unresolved requests intact.

Not every installed version has all listed hooks. Check the installed version's hook documentation; do not infer newer support from this template. Copilot also understands some Claude-format repository settings, so installing both agents' templates in the same repository can produce cross-loaded hooks. Prefer each product's user-only scope when sharing repositories, and inspect actual source classification. Claude-shaped payloads generated by Copilot include timestamps; this adapter is not a reliable automatic host detector.

To uninstall, remove only the generated entries whose exact command matches the Agent Lamp wrapper invocation. If a matcher group becomes empty, remove that group. Preserve all remaining events and settings. Restart Claude Code.

Copilot desktop extension template

The maintained source is integrations/copilot-desktop/extension.ts. The build emits dist/integrations/copilot-desktop/extension.mjs with the filename required by the SDK. Both are outside .github/extensions/ deliberately, so opening or building this repository does not activate the observer.

  1. Run npm run build. The generated entry pins absolute, URL-encoded imports to the compiled runtime and an absolute path to .agent-lamp/config.json. For a different configuration file, set AGENT_LAMP_CONFIG=/absolute/file.json when running the build. This reads no agent settings.
  2. Create a dedicated agent-lamp extension directory in the app's supported project/session discovery location only when you opt in. Copy the generated entry there as extension.mjs. Preserve unrelated extension directories. Keep the durable checkout and its dist/ output available. Don't install into a shared global discovery scope unless you understand that Copilot CLI may load it too.
  3. Use the app's extension reload/start mechanism yourself and inspect its logs. No reload or installation is performed by this project.

The SDK resolves @github/copilot-sdk/extension. There is no npm SDK dependency to install. joinSession({}) and generic session.on(handler) are the only host calls. No tools, permission handlers, user-input handlers, hooks, registerInterest, prompt mutation or message injection are registered.

The small declaration under types/ describes only the SDK methods this observer consumes. Rebuild and recopy the generated entry after relocating the checkout or changing the config path. There is no authored .mjs implementation to edit.

Each observer uses session.sessionId. Desktop request/completion events use matching requestId values, independently of tool activity or other requests in the same session. Permissions require a promptRequest descriptor and must not have resolvedByHook: true. Versions that omit the descriptor can miss permissions rather than claiming that an automatically handled decision needs a human. User-input and elicitation event pairs use their own namespaces.

An extension observes only its attached SDK session, not the entire desktop app. App session processes may each need their own extension instance. Copilot desktop and terminal share SDK discovery conventions; there is no proven universal automatic host discriminator here. Do not load this template in a CLI session and also enable the CLI command hooks, or one session will be counted as two different sources. Install in a known desktop-only scope and confirm with a small local task.

The observer keeps at most 256 normalized events in memory and retries bridge delivery every two seconds. It does not persist raw SDK events. Overflow emits observer_queue_full_event_lost; reconcile visible agent state after an outage. SDK subscription before an event was emitted is not guaranteed, and this observer does not fetch conversation history or take over UI to reconstruct missed transient questions. The bridge preserves already-known requests across restarts.

To uninstall, remove only the extension directory you created and reload or restart that app session. Stop the bridge separately. It does not automatically switch the physical lamp off on bridge exit: the last WLED command can remain visible, so use WLED's own controls if you want it off.