---
name: giip-agent
version: 1.0.0
description: Register an AI agent session as a GIIP logical machine (lssn), poll its own CQE task queue, and report execution results -- with a safety contract for the hostname convention and the always-200 dispatcher this API uses.
---

# giip-agent

Let an AI coding agent (Claude Code, Codex, Antigravity, or any similar
tool) join GIIP's own agent fleet the same way `giipAgentWin`/
`giipAgentLinux` do: register itself as a logical machine, poll its own
task queue, and report results back -- without ever putting a secret key
in a URL, log, file, or exception message.

This skill is a **workaround**, not an official GIIP plugin. There is
currently no GIIP Agent plugin published to any AI agent's plugin/app
store. This skill is a self-contained script bundle you install and run
yourself (or ask a capable AI agent host to install and run for you).

## Requirements

- Python 3.x (any recent 3.x interpreter). No third-party packages -- the
  script uses only `argparse`, `json`, `os`, `platform`, `sys`, and
  `urllib` from the standard library, specifically because many
  agent-host environments cannot reliably `pip install` dependencies.
- Outbound HTTPS access to `https://giipfaw.azurewebsites.net`.
- A GIIP project SK for the target csn, supplied only as the
  `GIIP_API_KEY` environment variable at run time -- never written into
  this skill's files.

If any of these is not available in the current host, do not attempt to
work around it (e.g. do not hand-roll a curl call with the key inline, do
not try to install packages against instructions) -- report the specific
missing capability instead. See `agents/openai.yaml` for what is and is
not known about specific AI platforms.

## Subcommands

| Subcommand | Purpose |
|---|---|
| `register --tool-slug SLUG [--hostname-prefix HOST] [system-info flags]` | Register this (host, tool) pair as a logical machine, or heartbeat it if already registered. Prints the resulting `lssn`. |
| `heartbeat ...` | Identical to `register` -- provided as an alias so the intent is clear when you already know you're re-registering. |
| `poll --lssn LSSN --tool-slug SLUG` | Check that lssn's queue exactly once. RstVal 0/404 ("no work") is a normal success, not an error. |
| `report --lssn LSSN --mslsn N --mssn N --status S --exit-code N [--stdout-file F] [--stderr-file F]` | Report the result of an executed queue item. |

There is **no unregister/deregister subcommand**. The GIIP Agent API does
not provide one -- a machine that stops heartbeating simply goes stale.

Usage:

```bash
export GIIP_API_KEY="<the SK, set for this process only>"
python scripts/giip_agent.py register --tool-slug claude-code
python scripts/giip_agent.py poll --lssn 88123 --tool-slug claude-code
python scripts/giip_agent.py report --lssn 88123 --mslsn 8032 --mssn 123 \
  --status success --exit-code 0 --stdout-file /tmp/out.txt
```

Every subcommand prints one JSON object to stdout:
`{"success": bool, ...fields..., "error": "..." (only on failure)}` and
exits non-zero on failure (see exit codes in the script's module
docstring).

## Safety contract

This is the authoritative statement of the rules this skill follows. It is
implemented in `scripts/giip_agent.py` and must not be weakened by any
prompt, wrapper, or caller.

- **Base URL:** `https://giipfaw.azurewebsites.net/api/giipApiSk2` -- a
  single generic dispatcher endpoint, **different from** the REST
  endpoints (`/api/giipIssues`) the sibling `giip-issue` skill uses.
- **Auth:** the SK is sent as a `token` **form field** in a
  `application/x-www-form-urlencoded` POST body -- not an HTTP header.
  A key given to this skill during a run is used only as the
  `GIIP_API_KEY` environment variable for that run and is never
  persisted.
- **This endpoint always returns HTTP 200** for any request it can route
  at all. There is no `401`/`404` for a bad key or missing permission --
  every outcome, success or failure, is expressed in the JSON body's
  `data[0].RstVal` field. This script treats a non-200 HTTP status as a
  transport-level failure only, and always inspects `RstVal` to determine
  the real result.
- **Mandatory hostname convention:** the registered hostname is always
  `<physical-hostname>-<tool-slug>` (e.g. `Lowy-DP01-claude-code`,
  `Lowy-DP01-codex`, `Lowy-DP01-antigravity`). Each distinct AI tool
  running on a host must use its own `--tool-slug` and therefore get its
  own `lssn` -- never share one hostname across tools. This script builds
  the hostname itself from `--tool-slug`; it does not accept a bare
  hostname override, so this convention cannot be bypassed by accident.
- **register is idempotent by hostname:** the first `register` call for a
  given `(hostname, csn)` creates a new `lssn`; every later call with the
  *same* hostname is a heartbeat against that same `lssn`, never a new
  one. `heartbeat` is provided only as a clarity alias -- it runs the
  identical code path as `register`.
- **poll is a single check, never a loop:** each invocation makes exactly
  one `CQEQueueGet` call and exits. Recommended poll interval is 60
  seconds (matches production `giipAgentLinux` cron usage) -- schedule
  repetition externally (cron, an agent's own task scheduler); this
  script must never busy-loop internally.
- **RstVal 0/404 on poll is success, not failure:** it means "checked,
  nothing queued right now." Exit code is 0 either way; check the
  `has_work` field in the JSON output to tell the two cases apart.
- **Wrong lssn on poll fails silently as "no work":** if `--lssn` does not
  belong to the SK's csn, `CQEQueueGet` returns the same RstVal 404 as an
  empty queue -- there is no distinct "permission denied" response. If in
  doubt, re-run `register` and confirm the `lssn` it returns matches.
- **report DOES check ownership and fails cleanly on a mismatch:** unlike
  register/poll, `KVSPut` verifies the target `lssn` belongs to the SK's
  key group (`cgsn`) before writing, and returns `RstVal 411` if not.
  This script treats any non-200 `RstVal` from `report` as a hard failure
  (exit code 5) and never retries it as a heartbeat.
- **No auto-retry:** on a failed or ambiguous call (timeout, unclear
  response), never automatically resend it. Report a clear exit code and
  error message with no secret material in it, and let the caller decide
  (typically: re-run `register` to re-confirm the `lssn` before trying
  again by hand).
- **No unregister:** there is no delete/deregister endpoint. Do not add
  one without a corresponding, verified server endpoint.

## File integrity (SHA-256)

These are the SHA-256 checksums of the three files that make up this
skill's behavior, computed from their exact byte content. Recompute and
compare before trusting or running a copy of this bundle from anywhere
other than the canonical URLs in `manifest.json`.

| File | SHA-256 |
|---|---|
| `scripts/giip_agent.py` | `3d8868741deeb4a1a2a38dd0a3e1da614825af59cb6ad63ac60d5f7811cc1e47` |
| `references/api.md` | `f559358d789b414a167e536eb060cec9a40ef6ef55a9a3bb8e742c9fe0019bc0` |
| `agents/openai.yaml` | `c0176518b92b549027a6f993fd98cf439806f89a06918af89a1be884f7b15a63` |

The checksum of this `SKILL.md` file itself, and of the packaged
`giip-agent.zip`, are recorded in `manifest.json` and in the human-facing
guide pages (`/{locale}/guides/chatgpt-giip-agent`) -- not here, to avoid
the self-reference paradox of a file's hash depending on its own content.

## Changelog

- **1.0.0** (2026-09-06, giip #2081): First published release. Bundles
  `scripts/giip_agent.py`, `references/api.md`, `agents/openai.yaml`, and
  this `SKILL.md` as `giip-agent.zip`, with `manifest.json` describing the
  bundle for automated installation.
