pks-agent-notch

alpha

A notch for Windows: a top-centre always-on-top pill that pops out cards (questions, tasks, facts, permissions) from local agents and reports your clicks back over a loopback API.

Author: Poul Kjeldager
Usage: pks-agent-notch [--bottom] | pks-agent-notch demo [--show] [--scene <name>] [--list] | pks-agent-notch --version | pks-agent-notch --help
Platform: windows
Category: agents

Examples

$ pks-agent-notch

Start the notch at the top of the primary screen (one instance; a second start exits)

$ pks-agent-notch --bottom

Start it docked at the bottom, over the taskbar

$ pks exec pks-agent-notch

Launch it through pks; the discovery manifest declares no capabilities, so nothing is asked

$ pks-agent-notch demo | Out-Host

Scripted tour of every card kind, a five-field question, ttl, replace, reply_to, the answer API and history — PASS/FAIL per scene, exit 0/1

$ pks-agent-notch demo --show

The same tour with no expectations, paced for a screen recording

$ curl -s -X POST http://127.0.0.1:$(cat $LOCALAPPDATA/pks-notch/port)/v1/cards -d '{"source":"demo","kind":"spørgsmål","title":"Virker det?","actions":[{"id":"yes","label":"Ja"}]}'

Post a card from anything local

A Mac-notch-style pill at the top centre of your Windows screen. Agents and tools on the same machine post cards to it; you see each card pop out for a few seconds, hover to see them all, and click a button to answer. The notch never takes focus from the window you are typing in, so an agent in a terminal you cannot see can still ask.

A card with fields is a question: the person types or picks the answer on the card itself, and the asking tool reads it back by id. Cards have a kind that sets the badge colour: spørgsmål (orange), opgave (amber), fakta and info (muted), svar (green), tilladelse (red). The pill reads ● <source> · N kort, or ● <source> · 1 spørgsmål · N kort while something waits for an answer.

Install

irm https://agentics.dk/install/pks-agent-notch.ps1 | iex
pks-agent-notch

Windows 10/11, x64. Self-contained single file; nothing else to install.

Run

CommandDoes
pks-agent-notchStart at the top of the primary screen
pks-agent-notch --bottomStart docked at the bottom
pks-agent-notch --versionPrint the version
pks-agent-notch --helpPrint the usage
pks exec pks-agent-notchSame, launched by pks (no provider is needed, so none is asked)
pks-agent-notch demo | Out-HostA scripted tour against the running notch (started if needed): PASS/FAIL per scene, exit 0 only when all passed
pks-agent-notch demo --showThe same tour, no expectations, paced for a recording (--pace <s>)
pks-agent-notch demo --scene <name>One scene; --list names them; --timeout <s> is how long a scene waits for your click (90)

A plain start prints nothing: the notch is a window without a console. What you should see is the pill at the top of the screen and a tray icon. When neither appears, read %LOCALAPPDATA%\pks-notch\notch.log — every start, exit and unhandled exception is a line there.

The demo

pks-agent-notch demo is the acceptance test, the product demo and a reference client in one. Ten scenes: health (the health document and the 404 route list), kinds (all six kinds live at once, then deleted), klik (you click one of two buttons — status and the SSE event must agree), luk (you close a tilladelse with ✕ → dismissed), ttl (ttl_s → expired), erstat (same id twice → one card, the first replaced in history), felter (a question with all five field types — you fill it in), svar-api (POST …/answer: 400 without the required value, 200 with it, 409 the second time), reply-to (a loopback listener receives the status; http://example.com is refused) and historik (every card of the run, with outcome and values). Pipe it from PowerShell (| Out-Host) or run it as pks exec pks-agent-notch demo — a window app otherwise hands the prompt back at once. Exit codes: 0 all passed · 1 a scene failed · 2 no notch to talk to · 3 bad flags.

Right-click the notch, or the tray icon: Dock top · Dock bottom · Afslut. Hover for the full list; the ✕ chip top-right hides the notch to a sliver until the next card (at least 10 s).

The API

While it runs, the notch serves http://127.0.0.1:<port>/ and writes the port to %LOCALAPPDATA%\pks-notch\port (one line; deleted on exit).

$port = Get-Content "$env:LOCALAPPDATA\pks-notch\port"
$base = "http://127.0.0.1:$port"
RouteDoes
POST /v1/cards{id?, source, kind, title, text?, fields?, actions?:[{id,label,role?}], ttl_s?, reply_to?} → 201 {id}. An existing id replaces the card in place.
GET /v1/cardsThe current list, newest first
GET /v1/cards/{id}The card and its state (pending · answered · dismissed · expired · deleted), with answer: {action, values} once it ended
POST /v1/cards/{id}/answer{action?, values?} — answer on the person's behalf, same validation as the screen → 200 status
DELETE /v1/cards/{id}Remove a card → 204
GET /v1/eventstext/event-stream: event: action with {"card","action","source","values"?} on every click or answer, ✕ included as dismiss; : keepalive every 15 s
GET /v1/historyThe last 500 cards that ended, with outcome, resolved_at and values
GET /v1/health{version, cards, questions, history, uptime_s}

Asking a question

A card with fields is a question the person answers on the card: text (placeholder, suggestions, multiline), choice (one of), multi (any of), toggle (ja/nej), number (min/max). The submit action is dimmed until the required fields are filled; a secondary action ("Senere") sends whatever is there. Read the answer back from GET /v1/cards/{id}, the event stream, or give a reply_to URL (https:// anywhere, http:// only on this machine) and the notch POSTs the status when the card ends.

Invoke-RestMethod -Method Post -Uri "$base/v1/cards" -ContentType 'application/json' -Body '{
  "id":"voice-1","source":"heypoul","kind":"spørgsmål","title":"Hvem er det, der taler?",
  "text":"Ukendt stemme i 70 s.",
  "fields":[{"id":"name","type":"text","label":"Navn","required":true,"suggestions":["Anders Hjorth","Malthe Schou"]},
            {"id":"keep","type":"toggle","label":"Gem stemmen","default":true}],
  "actions":[{"id":"save","label":"Gem"},{"id":"later","label":"Senere"}],"ttl_s":900}'
Invoke-RestMethod "$base/v1/cards/voice-1"
# state: answered, answer: {action: save, values: {name: "Anders Hjorth", keep: true}}

Post a question and wait for the answer:

Invoke-RestMethod -Method Post -Uri "$base/v1/cards" -ContentType 'application/json' -Body '{
  "id":"q-1","source":"demo","kind":"spørgsmål","title":"Skal vi sende tilbuddet mandag?",
  "actions":[{"id":"yes","label":"Ja, mandag"},{"id":"later","label":"Senere"}],"ttl_s":120}'
curl.exe -sN "$base/v1/events"
# event: action
# data: {"card":"q-1","action":"yes","source":"demo"}

A click resolves the card (it leaves the list) before the event is sent; re-POST with the same id to keep it. English kind names (question, task, fact, answer, permission, info) are accepted and stored as the Danish ones. No auth in v0: loopback, same user.

The full contract with every status code is in the repo's docs/protocol.md.

Sources

  • heypoul — the first source: insight cards from a live meeting transcript.
  • Claude Code hooks — next: permission requests and questions from a coding agent.

Anything that can do an HTTP POST on the same machine is a source.