=== census: who else states the condition === docs/MANIFEST.md:148:run --resume `, or the picker's *Resume from history*) **and** the docs-site/book/searchindex-d0ef83b9.js:1:window.search = Object.assign(window.search, JSON.parse('{"doc_urls":["index.html#spt-developer-docs","index.html#install","index.html#how-these-docs-are-organized","index.html#for-ai-agents-reading-this","quickstart/messaging.html#quickstart-two-agents-exchange-a-message","quickstart/messaging.html#1-install-one-line","quickstart/messaging.html#2-optional-link-two-machines","quickstart/messaging.html#3-hand-your-receiver-agent-its-prompt","quickstart/messaging.html#4-hand-your-sender-agent-its-prompt","quickstart/messaging.html#5-deliver-to-someone-whos-offline","quickstart/messaging.html#6-what-just-happened","quickstart/messaging.html#next","quickstart/adapter.html#quickstart-build-an-adapter","quickstart/adapter.html#0-what-an-adapter-is","quickstart/adapter.html#1-get-the-reference-adapter","quickstart/adapter.html#2-read-the-manifest","quickstart/adapter.html#3-validate-and-register","quickstart/adapter.html#4-drive-the-contract","quickstart/adapter.html#5-make-it-yours","quickstart/adapter.html#next","concepts/overview.html#mental-model","concepts/overview.html#the-shape-of-the-system","concepts/overview.html#endpoints-and-perches","concepts/overview.html#messaging","concepts/overview.html#the-daemon-broker-and-brain","concepts/overview.html#live-agents-and-the-mind","concepts/overview.html#instances-dormancy-and-rest","concepts/overview.html#subnets-pairing-and-the-network","concepts/overview.html#the-harness-contract","concepts/overview.html#self-update","concepts/overview.html#where-to-go-next","messaging/overview.html#messaging","messaging/overview.html#semantics","messaging/overview.html#send-outcomes--the-closed-set","messaging/overview.html#the-event-wire-contract","messaging/overview.html#addressing","messaging/overview.html#commands","lifecycle/overview.html#live-agent-lifecycle","lifecycle/overview.html#the-pieces","lifecycle/overview.html#rest-and-wake","lifecycle/overview.html#reading-the-picker","lifecycle/overview.html#commands","terminal/overview.html#terminal-hosting","terminal/overview.html#what-the-broker-holding-the-pty-buys","terminal/overview.html#commands","networking/overview.html#networking--subnets","networking/overview.html#the-model","networking/overview.html#the-walkthrough","networking/overview.html#troubleshooting-a-join","networking/overview.html#what-rides-it","networking/overview.html#commands","harness-contract/overview.html#harness-contract","harness-contract/overview.html#where-to-go","harness-contract/integration-checklist.html#harness-integration-checklist","harness-contract/integration-checklist.html#the-interaction-lifecycle","harness-contract/integration-checklist.html#group-1--required-no-adapter-exists-without-these","harness-contract/integration-checklist.html#group-2--recommended-the-integration-is-hollow-without-them","harness-contract/integration-checklist.html#group-3--optional-capability-specific","harness-contract/integration-checklist.html#group-4--beyond-the-api-integrations-that-make-it-good","harness-contract/integration-checklist.html#patterns-introduced-in-v0160","harness-contract/integration-checklist.html#hook-dispatch-by-resolve-not-execute","harness-contract/integration-checklist.html#incremental-digest-consumption--the---json-cursor","harness-contract/integration-checklist.html#global---json-for-readstatus-commands","harness-contract/integration-checklist.html#am-i-done--the-floor","harness-contract/integration-checklist.html#next","harness-contract/manifest.html#manifest-reference","harness-contract/manifest.html#the-principle","harness-contract/manifest.html#substitution-keys","harness-contract/manifest.html#adapter--header-required","harness-contract/manifest.html#hooksevent--inbound-hook-table","harness-contract/manifest.html#session--watched-dirs--role-templates","harness-contract/manifest.html#sessionrole--outbound-templates","harness-contract/manifest.html#envvar--env-var-table","harness-contract/manifest.html#history--transcript-access","harness-contract/manifest.html#digest--session-digest-extractor","harness-contract/manifest.html#inject--input-injection-methods","harness-contract/manifest.html#message-idle-translation-binary--spt-hosted-idle-delivery","harness-contract/manifest.html#identity--session-identity","harness-contract/manifest.html#session-digest--the-digest-record-contract","harness-contract/manifest.html#strings--adapter-string-values--profiles","harness-contract/manifest.html#update--adapter-self-update","harness-contract/manifest.html#gh_release--ship-updates-from-your-github-releases-since-v080","harness-contract/manifest.html#shell-adapters-kind--shell","harness-contract/manifest.html#cross-field-rules-spt-adapter-add-enforces-these","harness-contract/api.html#the-spt-api-surface","harness-contract/api.html#the-two-startup-flows","harness-contract/api.html#session-lifecycle","harness-contract/api.html#api-seed---pid-pid---session-id-id","harness-contract/api.html#api-listen-id---once---parent-pid-pid---subnet-name","harness-contract/api.html#api-bind-id---set-session-id-sid","harness-contract/api.html#api-boundary-clearcompact-id---to-session-id-new-sid---session-id-prior-sid","harness-contract/api.html#api-psyche-download-id---session-id-sid","harness-contract/api.html#api-session-end-id---erase","harness-contract/api.html#api-shutdown-id","harness-contract/api.html#activity-and-presence","harness-contract/api.html#api-state-busyidle-id---no-gate","harness-contract/api.html#api-echo-gate-setclear-id","harness-contract/api.html#api-presence-id","harness-contract/api.html#api-driven-by-id","harness-contract/api.html#messages","harness-contract/api.html#api-poll-id---include-deferred---link-token","harness-contract/api.html#api-history-log-id","harness-contract/api.html#workers","harness-contract/api.html#api-worker-start-parent---agent-id-id---agent-type-type","harness-contract/api.html#api-worker-stop-id---session-id-sid--api-worker-poll-id---session-id-sid","harness-contract/api.html#shells","harness-contract/api.html#api-bind-shell---link-token","harness-contract/api.html#api-emit-id-payload---type-type---link-token","harness-contract/api.html#api-owner-shutdown-id---link-token","harness-contract/api.html#introspection","harness-contract/api.html#api-capability","harness-contract/api.html#conventions","harness-contract/echo-commune.html#echo-commune--the-io-contract","harness-contract/echo-commune.html#the-role","harness-contract/echo-commune.html#keys-spt-core-fills","harness-contract/echo-commune.html#history-is-not-fed-on-stdin","harness-contract/echo-commune.html#self-locating-the-harness","harness-contract/echo-commune.html#drop-file-protocol","harness-contract/echo-commune.html#what-spt-core-expects-on-stdout","harness-contract/echo-commune.html#in-one-line","harness-contract/install-on-demand.html#install-on-demand-bootstrap","harness-contract/install-on-demand.html#the-generic-contract","harness-contract/install-on-demand.html#check-and-install-posix-sh","harness-contract/install-on-demand.html#check-and-install-powershell","harness-contract/install-on-demand.html#activate-the-adapter--register-your-manifest","harness-contract/install-on-demand.html#the-windows-path-refresh-gotcha","harness-contract/install-on-demand.html#pinning-and-air-gapped-installs","harness-contract/install-on-demand.html#trust-model","harness-contract/patterns.html#adapter-patterns--pitfalls","harness-contract/patterns.html#the-one-rule-manifests-are-static-logic-lives-in-binaries","harness-contract/patterns.html#the-adapter-lives-in-the-registry","harness-contract/patterns.html#profiles-are-sparse-leaf-replace-overlays","harness-contract/patterns.html#wiring-hooks-you-own-the-harness-side","harness-contract/patterns.html#get-these-right-in-the-hook-layer","harness-contract/patterns.html#strings-keep-the-manifest-thin-point-at-the-live-binary","harness-contract/patterns.html#digest-the-transcriptrecord-extractor","harness-contract/patterns.html#the-bringup--launcher-seam","harness-contract/patterns.html#the-live-agent-companion-seam","harness-contract/patterns.html#prove-live-bringup-non-interactively","harness-contract/patterns.html#lifecycle-continuity-is-file-drops","harness-contract/patterns.html#testing-against-a-real-harness-isolate-identity","harness-contract/patterns.html#validate-against-the-live-binary","harness-contract/patterns.html#next","instances/overview.html#instances","instances/overview.html#the-rules-that-keep-it-sane","instances/overview.html#commands","shells/overview.html#shells","shells/overview.html#the-model-in-five-facts","shells/overview.html#four-channels-between-owner-and-shell","shells/overview.html#two-safety-properties","shells/overview.html#start-here","shells/getting-started.html#getting-started-a-notification-shell","shells/getting-started.html#1-install-and-spawn-it","shells/getting-started.html#2-drive-it-from-an-agent","shells/getting-started.html#3-read-the-manifest","shells/getting-started.html#4-make-your-own","self-update/overview.html#self-update","self-update/overview.html#the-invariant","self-update/overview.html#the-trust-chain","self-update/overview.html#how-updates-move","self-update/overview.html#composite-adapter-updates--a-delegated-post-step-since-v0160","self-update/overview.html#commands","cli/reference.html#cli-reference","cli/reference.html#spt","cli/reference.html#spt-adapter","cli/reference.html#spt-adapter-add","cli/reference.html#spt-adapter-remove","cli/reference.html#spt-adapter-list","cli/reference.html#spt-adapter-version","cli/reference.html#spt-adapter-create-profile","cli/reference.html#spt-adapter-delete-profile","cli/reference.html#spt-adapter-get-string","cli/reference.html#spt-adapter-digest-proof","cli/reference.html#spt-adapter-translate-proof","cli/reference.html#spt-adapter-set-string","cli/reference.html#spt-adapter-update","cli/reference.html#spt-adapter-use","cli/reference.html#spt-daemon","cli/reference.html#spt-daemon-run","cli/reference.html#spt-daemon-start","cli/reference.html#spt-daemon-stop","cli/reference.html#spt-daemon-status","cli/reference.html#spt-grant","cli/reference.html#spt-grant-add","cli/reference.html#spt-grant-revoke","cli/reference.html#spt-grant-list","cli/reference.html#spt-notif","cli/reference.html#spt-notif-list","cli/reference.html#spt-notif-dismiss","cli/reference.html#spt-rc","cli/reference.html#spt-subnet","cli/reference.html#spt-subnet-status","cli/reference.html#spt-subnet-create","cli/reference.html#spt-subnet-show-code","cli/reference.html#spt-subnet-join","cli/reference.html#spt-subnet-leave","cli/reference.html#spt-subnet-prune","cli/reference.html#spt-subnet-revoke","cli/reference.html#spt-subnet-detach","cli/reference.html#spt-subnet-attach","cli/reference.html#spt-subnet-notify","cli/reference.html#spt-update","cli/reference.html#spt-update-apply","cli/reference.html#spt-update-fetch","cli/reference.html#spt-api","cli/reference.html#spt-api-seed","cli/reference.html#spt-api-listen","cli/reference.html#spt-api-bind","cli/reference.html#spt-api-bind-shell","cli/reference.html#spt-api-state","cli/reference.html#spt-api-echo-gate","cli/reference.html#spt-api-poll","cli/reference.html#spt-api-psyche-download","cli/reference.html#spt-api-worker-start","cli/reference.html#spt-api-worker-stop","cli/reference.html#spt-api-worker-poll","cli/reference.html#spt-api-boundary","cli/reference.html#spt-api-session-end","cli/reference.html#spt-api-presence","cli/reference.html#spt-api-driven-by","cli/reference.html#spt-api-endpoint-info","cli/reference.html#spt-api-history-log","cli/reference.html#spt-api-digest-entry","cli/reference.html#spt-api-emit","cli/reference.html#spt-api-drive-poll","cli/reference.html#spt-api-tunnel","cli/reference.html#spt-api-capability","cli/reference.html#spt-api-hint","cli/reference.html#spt-api-shutdown","cli/reference.html#spt-api-owner-shutdown","cli/reference.html#spt-endpoint","cli/reference.html#spt-endpoint-list","cli/reference.html#spt-endpoint-run","cli/reference.html#spt-endpoint-fork","cli/reference.html#spt-endpoint-suspend","cli/reference.html#spt-endpoint-wake","cli/reference.html#spt-endpoint-shutdown","cli/reference.html#spt-endpoint-stop","cli/reference.html#spt-endpoint-rename","cli/reference.html#spt-endpoint-purge","cli/reference.html#spt-endpoint-digest","cli/reference.html#spt-endpoint-access","cli/reference.html#spt-endpoint-description","cli/reference.html#spt-endpoint-role","cli/reference.html#spt-how-to","cli/reference.html#spt-ready","cli/reference.html#spt-ring","cli/reference.html#spt-send","cli/reference.html#spt-shell","cli/reference.html#spt-shell-spawn","cli/reference.html#spt-shell-list","cli/reference.html#spt-shell-teardown","cli/reference.html#spt-shell-rename","cli/reference.html#spt-shell-cmd","cli/reference.html#spt-shell-drive","cli/reference.html#spt-shell-tunnel","cli/reference.html#spt-shell-send","cli/reference.html#spt-shell-relink","cli/reference.html#spt-whoami","reference/json-shapes.html#json-output-shapes","reference/json-shapes.html#send-outcomes","reference/json-shapes.html#session-digest--endpoint-digest---json","reference/json-shapes.html#the-shell-relay--mac-stamped-frames","reference/json-shapes.html#--json-catalog","reference/schema.html#manifest-json-schema","reference/install.html#install-scripts","reference/install.html#what-they-do","reference/install.html#environment-knobs","reference/install.html#trust-model","reference/install.html#os-service-registration"],"index":{"documentStore":{"docInfo":{"0":{"body":79,"breadcrumbs":6,"title":3},"1":{"body":21,"breadcrumbs":4,"title":1},"10":{"body":114,"breadcrumbs":7,"title":2},"100":{"body":65,"breadcrumbs":12,"title":7},"101":{"body":14,"breadcrumbs":9,"title":4},"102":{"body":18,"breadcrumbs":6,"title":1},"103":{"body":87,"breadcrumbs":15,"title":10},"104":{"body":50,"breadcrumbs":19,"title":14},"105":{"body":13,"breadcrumbs":6,"title":1},"106":{"body":18,"breadcrumbs":10,"title":5},"107":{"body":25,"breadcrumbs":13,"title":8},"108":{"body":30,"breadcrumbs":11,"title":6},"109":{"body":0,"breadcrumbs":6,"title":1},"11":{"body":43,"breadcrumbs":6,"title":1},"110":{"body":16,"breadcrumbs":7,"title":2},"111":{"body":45,"breadcrumbs":6,"title":1},"112":{"body":88,"breadcrumbs":10,"title":4},"113":{"body":115,"breadcrumbs":7,"title":1},"114":{"body":114,"breadcrumbs":10,"title":4},"115":{"body":93,"breadcrumbs":9,"title":3},"116":{"body":137,"breadcrumbs":9,"title":3},"117":{"body":197,"breadcrumbs":9,"title":3},"118":{"body":97,"breadcrumbs":10,"title":4},"119":{"body":27,"breadcrumbs":8,"title":2},"12":{"body":50,"breadcrumbs":6,"title":3},"120":{"body":53,"breadcrumbs":8,"title":3},"121":{"body":50,"breadcrumbs":7,"title":2},"122":{"body":36,"breadcrumbs":9,"title":4},"123":{"body":36,"breadcrumbs":8,"title":3},"124":{"body":537,"breadcrumbs":9,"title":4},"125":{"body":57,"breadcrumbs":9,"title":4},"126":{"body":53,"breadcrumbs":9,"title":4},"127":{"body":29,"breadcrumbs":7,"title":2},"128":{"body":62,"breadcrumbs":8,"title":3},"129":{"body":100,"breadcrumbs":12,"title":7},"13":{"body":46,"breadcrumbs":5,"title":2},"130":{"body":65,"breadcrumbs":8,"title":3},"131":{"body":103,"breadcrumbs":10,"title":5},"132":{"body":193,"breadcrumbs":9,"title":4},"133":{"body":213,"breadcrumbs":8,"title":3},"134":{"body":134,"breadcrumbs":12,"title":7},"135":{"body":172,"breadcrumbs":8,"title":3},"136":{"body":89,"breadcrumbs":8,"title":3},"137":{"body":227,"breadcrumbs":9,"title":4},"138":{"body":210,"breadcrumbs":10,"title":5},"139":{"body":49,"breadcrumbs":9,"title":4},"14":{"body":54,"breadcrumbs":6,"title":3},"140":{"body":125,"breadcrumbs":11,"title":6},"141":{"body":295,"breadcrumbs":9,"title":4},"142":{"body":26,"breadcrumbs":6,"title":1},"143":{"body":26,"breadcrumbs":2,"title":1},"144":{"body":123,"breadcrumbs":4,"title":3},"145":{"body":31,"breadcrumbs":2,"title":1},"146":{"body":28,"breadcrumbs":2,"title":1},"147":{"body":130,"breadcrumbs":4,"title":3},"148":{"body":136,"breadcrumbs":6,"title":5},"149":{"body":98,"breadcrumbs":4,"title":3},"15":{"body":146,"breadcrumbs":6,"title":3},"150":{"body":17,"breadcrumbs":3,"title":2},"151":{"body":48,"breadcrumbs":9,"title":4},"152":{"body":58,"breadcrumbs":8,"title":3},"153":{"body":80,"breadcrumbs":8,"title":3},"154":{"body":164,"breadcrumbs":8,"title":3},"155":{"body":149,"breadcrumbs":7,"title":2},"156":{"body":12,"breadcrumbs":4,"title":2},"157":{"body":28,"breadcrumbs":3,"title":1},"158":{"body":85,"breadcrumbs":4,"title":2},"159":{"body":94,"breadcrumbs":4,"title":2},"16":{"body":75,"breadcrumbs":6,"title":3},"160":{"body":118,"breadcrumbs":9,"title":7},"161":{"body":9,"breadcrumbs":3,"title":1},"162":{"body":18,"breadcrumbs":4,"title":2},"163":{"body":169,"breadcrumbs":3,"title":1},"164":{"body":465,"breadcrumbs":4,"title":2},"165":{"body":174,"breadcrumbs":5,"title":3},"166":{"body":63,"breadcrumbs":5,"title":3},"167":{"body":44,"breadcrumbs":5,"title":3},"168":{"body":53,"breadcrumbs":5,"title":3},"169":{"body":97,"breadcrumbs":6,"title":4},"17":{"body":93,"breadcrumbs":6,"title":3},"170":{"body":51,"breadcrumbs":6,"title":4},"171":{"body":73,"breadcrumbs":5,"title":3},"172":{"body":171,"breadcrumbs":6,"title":4},"173":{"body":203,"breadcrumbs":6,"title":4},"174":{"body":74,"breadcrumbs":6,"title":4},"175":{"body":80,"breadcrumbs":5,"title":3},"176":{"body":91,"breadcrumbs":5,"title":3},"177":{"body":145,"breadcrumbs":4,"title":2},"178":{"body":52,"breadcrumbs":5,"title":3},"179":{"body":49,"breadcrumbs":5,"title":3},"18":{"body":65,"breadcrumbs":6,"title":3},"180":{"body":72,"breadcrumbs":5,"title":3},"181":{"body":43,"breadcrumbs":5,"title":3},"182":{"body":106,"breadcrumbs":4,"title":2},"183":{"body":89,"breadcrumbs":5,"title":3},"184":{"body":52,"breadcrumbs":5,"title":3},"185":{"body":39,"breadcrumbs":5,"title":3},"186":{"body":66,"breadcrumbs":4,"title":2},"187":{"body":41,"breadcrumbs":5,"title":3},"188":{"body":48,"breadcrumbs":5,"title":3},"189":{"body":109,"breadcrumbs":4,"title":2},"19":{"body":45,"breadcrumbs":4,"title":1},"190":{"body":140,"breadcrumbs":4,"title":2},"191":{"body":69,"breadcrumbs":5,"title":3},"192":{"body":68,"breadcrumbs":5,"title":3},"193":{"body":88,"breadcrumbs":6,"title":4},"194":{"body":109,"breadcrumbs":5,"title":3},"195":{"body":75,"breadcrumbs":5,"title":3},"196":{"body":83,"breadcrumbs":5,"title":3},"197":{"body":131,"breadcrumbs":5,"title":3},"198":{"body":80,"breadcrumbs":5,"title":3},"199":{"body":59,"breadcrumbs":5,"title":3},"2":{"body":28,"breadcrumbs":5,"title":2},"20":{"body":15,"breadcrumbs":4,"title":2},"200":{"body":95,"breadcrumbs":5,"title":3},"201":{"body":81,"breadcrumbs":4,"title":2},"202":{"body":73,"breadcrumbs":5,"title":3},"203":{"body":84,"breadcrumbs":5,"title":3},"204":{"body":519,"breadcrumbs":4,"title":2},"205":{"body":50,"breadcrumbs":5,"title":3},"206":{"body":75,"breadcrumbs":5,"title":3},"207":{"body":105,"breadcrumbs":5,"title":3},"208":{"body":64,"breadcrumbs":6,"title":4},"209":{"body":67,"breadcrumbs":5,"title":3},"21":{"body":92,"breadcrumbs":4,"title":2},"210":{"body":64,"breadcrumbs":6,"title":4},"211":{"body":83,"breadcrumbs":5,"title":3},"212":{"body":73,"breadcrumbs":6,"title":4},"213":{"body":99,"breadcrumbs":6,"title":4},"214":{"body":57,"breadcrumbs":6,"title":4},"215":{"body":57,"breadcrumbs":6,"title":4},"216":{"body":78,"breadcrumbs":5,"title":3},"217":{"body":61,"breadcrumbs":6,"title":4},"218":{"body":56,"breadcrumbs":5,"title":3},"219":{"body":57,"breadcrumbs":5,"title":3},"22":{"body":56,"breadcrumbs":4,"title":2},"220":{"body":60,"breadcrumbs":6,"title":4},"221":{"body":61,"breadcrumbs":6,"title":4},"222":{"body":73,"breadcrumbs":6,"title":4},"223":{"body":77,"breadcrumbs":5,"title":3},"224":{"body":92,"breadcrumbs":6,"title":4},"225":{"body":108,"breadcrumbs":5,"title":3},"226":{"body":36,"breadcrumbs":5,"title":3},"227":{"body":81,"breadcrumbs":5,"title":3},"228":{"body":68,"breadcrumbs":5,"title":3},"229":{"body":75,"breadcrumbs":6,"title":4},"23":{"body":36,"breadcrumbs":3,"title":1},"230":{"body":195,"breadcrumbs":4,"title":2},"231":{"body":117,"breadcrumbs":5,"title":3},"232":{"body":184,"breadcrumbs":5,"title":3},"233":{"body":118,"breadcrumbs":5,"title":3},"234":{"body":71,"breadcrumbs":5,"title":3},"235":{"body":72,"breadcrumbs":5,"title":3},"236":{"body":60,"breadcrumbs":5,"title":3},"237":{"body":52,"breadcrumbs":5,"title":3},"238":{"body":72,"breadcrumbs":5,"title":3},"239":{"body":90,"breadcrumbs":5,"title":3},"24":{"body":64,"breadcrumbs":5,"title":3},"240":{"body":110,"breadcrumbs":5,"title":3},"241":{"body":108,"breadcrumbs":5,"title":3},"242":{"body":85,"breadcrumbs":5,"title":3},"243":{"body":85,"breadcrumbs":5,"title":3},"244":{"body":56,"breadcrumbs":3,"title":1},"245":{"body":89,"breadcrumbs":4,"title":2},"246":{"body":69,"breadcrumbs":4,"title":2},"247":{"body":161,"breadcrumbs":4,"title":2},"248":{"body":273,"breadcrumbs":4,"title":2},"249":{"body":86,"breadcrumbs":5,"title":3},"25":{"body":81,"breadcrumbs":5,"title":3},"250":{"body":41,"breadcrumbs":5,"title":3},"251":{"body":47,"breadcrumbs":5,"title":3},"252":{"body":49,"breadcrumbs":5,"title":3},"253":{"body":73,"breadcrumbs":5,"title":3},"254":{"body":100,"breadcrumbs":5,"title":3},"255":{"body":92,"breadcrumbs":5,"title":3},"256":{"body":72,"breadcrumbs":5,"title":3},"257":{"body":55,"breadcrumbs":5,"title":3},"258":{"body":40,"breadcrumbs":4,"title":2},"259":{"body":56,"breadcrumbs":6,"title":3},"26":{"body":34,"breadcrumbs":5,"title":3},"260":{"body":47,"breadcrumbs":5,"title":2},"261":{"body":160,"breadcrumbs":8,"title":5},"262":{"body":120,"breadcrumbs":8,"title":5},"263":{"body":183,"breadcrumbs":5,"title":2},"264":{"body":112,"breadcrumbs":6,"title":3},"265":{"body":22,"breadcrumbs":4,"title":2},"266":{"body":63,"breadcrumbs":2,"title":0},"267":{"body":37,"breadcrumbs":4,"title":2},"268":{"body":27,"breadcrumbs":4,"title":2},"269":{"body":24,"breadcrumbs":5,"title":3},"27":{"body":45,"breadcrumbs":5,"title":3},"28":{"body":68,"breadcrumbs":4,"title":2},"29":{"body":34,"breadcrumbs":4,"title":2},"3":{"body":41,"breadcrumbs":6,"title":3},"30":{"body":26,"breadcrumbs":4,"title":2},"31":{"body":26,"breadcrumbs":2,"title":1},"32":{"body":274,"breadcrumbs":2,"title":1},"33":{"body":183,"breadcrumbs":5,"title":4},"34":{"body":213,"breadcrumbs":4,"title":3},"35":{"body":27,"breadcrumbs":2,"title":1},"36":{"body":23,"breadcrumbs":2,"title":1},"37":{"body":19,"breadcrumbs":6,"title":3},"38":{"body":195,"breadcrumbs":4,"title":1},"39":{"body":47,"breadcrumbs":5,"title":2},"4":{"body":41,"breadcrumbs":10,"title":5},"40":{"body":98,"breadcrumbs":5,"title":2},"41":{"body":42,"breadcrumbs":4,"title":1},"42":{"body":22,"breadcrumbs":4,"title":2},"43":{"body":183,"breadcrumbs":6,"title":4},"44":{"body":16,"breadcrumbs":3,"title":1},"45":{"body":26,"breadcrumbs":4,"title":2},"46":{"body":329,"breadcrumbs":3,"title":1},"47":{"body":46,"breadcrumbs":3,"title":1},"48":{"body":111,"breadcrumbs":4,"title":2},"49":{"body":26,"breadcrumbs":3,"title":1},"5":{"body":27,"breadcrumbs":9,"title":4},"50":{"body":29,"breadcrumbs":3,"title":1},"51":{"body":114,"breadcrumbs":4,"title":2},"52":{"body":52,"breadcrumbs":3,"title":1},"53":{"body":92,"breadcrumbs":7,"title":3},"54":{"body":33,"breadcrumbs":6,"title":2},"55":{"body":168,"breadcrumbs":10,"title":6},"56":{"body":406,"breadcrumbs":10,"title":6},"57":{"body":398,"breadcrumbs":9,"title":5},"58":{"body":226,"breadcrumbs":11,"title":7},"59":{"body":0,"breadcrumbs":7,"title":3},"6":{"body":144,"breadcrumbs":10,"title":5},"60":{"body":166,"breadcrumbs":8,"title":4},"61":{"body":365,"breadcrumbs":9,"title":5},"62":{"body":271,"breadcrumbs":8,"title":4},"63":{"body":162,"breadcrumbs":6,"title":2},"64":{"body":19,"breadcrumbs":5,"title":1},"65":{"body":43,"breadcrumbs":6,"title":2},"66":{"body":99,"breadcrumbs":5,"title":1},"67":{"body":339,"breadcrumbs":6,"title":2},"68":{"body":118,"breadcrumbs":7,"title":3},"69":{"body":99,"breadcrumbs":8,"title":4},"7":{"body":43,"breadcrumbs":10,"title":5},"70":{"body":38,"breadcrumbs":9,"title":5},"71":{"body":455,"breadcrumbs":7,"title":3},"72":{"body":38,"breadcrumbs":8,"title":4},"73":{"body":71,"breadcrumbs":7,"title":3},"74":{"body":314,"breadcrumbs":8,"title":4},"75":{"body":22,"breadcrumbs":8,"title":4},"76":{"body":406,"breadcrumbs":12,"title":8},"77":{"body":30,"breadcrumbs":7,"title":3},"78":{"body":124,"breadcrumbs":9,"title":5},"79":{"body":256,"breadcrumbs":9,"title":5},"8":{"body":94,"breadcrumbs":10,"title":5},"80":{"body":161,"breadcrumbs":8,"title":4},"81":{"body":156,"breadcrumbs":10,"title":6},"82":{"body":330,"breadcrumbs":8,"title":4},"83":{"body":107,"breadcrumbs":11,"title":7},"84":{"body":257,"breadcrumbs":8,"title":3},"85":{"body":157,"breadcrumbs":8,"title":3},"86":{"body":0,"breadcrumbs":7,"title":2},"87":{"body":19,"breadcrumbs":12,"title":7},"88":{"body":36,"breadcrumbs":14,"title":9},"89":{"body":126,"breadcrumbs":12,"title":7},"9":{"body":67,"breadcrumbs":10,"title":5},"90":{"body":225,"breadcrumbs":17,"title":12},"91":{"body":103,"breadcrumbs":12,"title":7},"92":{"body":21,"breadcrumbs":10,"title":5},"93":{"body":23,"breadcrumbs":8,"title":3},"94":{"body":0,"breadcrumbs":7,"title":2},"95":{"body":20,"breadcrumbs":10,"title":5},"96":{"body":22,"breadcrumbs":10,"title":5},"97":{"body":9,"breadcrumbs":8,"title":3},"98":{"body":12,"breadcrumbs":8,"title":3},"99":{"body":0,"breadcrumbs":6,"title":1}},"docs":{"0":{"body":"spt-core is a harness-independent core for an agent ecosystem: inter-agent\\nmessaging, live-agent lifecycle, terminal hosting, seamless self-update, and\\nzero-config cross-machine networking — shipped as a single canonical binary\\n( spt / spt.exe). It lets coding agents running under different harnesses talk to each other —\\nacross sessions, across projects, and across machines — with no central server. Pick your path: Developer — you want agents on your machines messaging each other:\\nstart with the messaging quickstart (one\\ninstall line + three commands, under 10 minutes). Adapter developer / dev-agent — you’re integrating a harness or\\nbuilding a shell against the public contract: start with the adapter quickstart, then the harness contract.","breadcrumbs":"SPT developer docs » SPT developer docs","id":"0","title":"SPT developer docs"},"1":{"body":"One line, non-interactive: # Linux\\ncurl -fsSL https://sabermage.github.io/spt-releases/install.sh | sh # Windows (PowerShell)\\nirm https://sabermage.github.io/spt-releases/install.ps1 | iex Verify: $ spt --version\\nspt 0.1.0","breadcrumbs":"SPT developer docs » Install","id":"1","title":"Install"},"10":{"body":"Perch — registering as sergey created a perch: a durable identity\\nwith an address and a spool, under spt-core’s per-machine home. spt list\\nshows every perch on the node, live or not. Live-first, spool-fallback — send tries a direct connection to the\\nregistered address first ( SENT); if the perch exists but no listener is\\nup, the message lands in the spool ( QUEUED) and is drained by the next ready. Reply routing — the sender id travels with every message\\nstructurally, surfaced as the arriving envelope’s from attribute; spt send lea answers the sender without\\nknowing anything else about them. Agents teach themselves — the prompt blocks point agents at spt how-to : task guidance shipped in the binary, so what an\\nagent reads can never disagree with the binary it runs. No daemon ceremony — you never started a server. Anything that needs\\nthe per-machine daemon auto-starts it on demand. Subnets carry it across machines — if you did step 2, these same\\nflows ride the paired P2P fabric: same commands, same outputs, machine\\nboundaries invisible.","breadcrumbs":"Quickstart: two agents exchange a message » 6. What just happened","id":"10","title":"6. What just happened"},"100":{"body":"Drain delivered messages over the hook channel (the pull-based path for\\nharnesses whose hooks can’t inject). Deferred-flagged rows are excluded\\nunless --include-deferred. With --link this is the shell-flavored drain:\\nthe link token authenticates, and the rows are the shell’s stamped\\ncommand/text/file frames. Authentication is required (rule 2 above): the drain must prove\\nassociation with --session-id (the perch’s recorded session) or a\\ncapability --token ( --link for the shell flavor). An\\nunauthenticated poll is refused with exit 1 and no output — messages\\nare addressed to the endpoint’s occupant, not to whoever asks.","breadcrumbs":"Harness contract » The spt api surface » api poll [--include-deferred] [--link ]","id":"100","title":"api poll [--include-deferred] [--link ]"},"101":{"body":"Append normalized history (body on stdin) to the endpoint’s native history\\nstore — the push half of [history] strategy = \\"native\\".","breadcrumbs":"Harness contract » The spt api surface » api history-log ","id":"101","title":"api history-log "},"102":{"body":"Nested, short-lived agents under a parent endpoint. A worker is process-local\\nmachinery — it authenticates with its parent’s session id and carries no\\ncapability token of its own.","breadcrumbs":"Harness contract » The spt api surface » Workers","id":"102","title":"Workers"},"103":{"body":"Create a nested worker perch under parent. The worker id is minted by\\nspt-core, not supplied by the caller: {parent}-w{N} with a persistent,\\nper-parent counter. The caller does not pass an id (a stray positional id is\\nrejected). Output channels follow the api status-line discipline: stdout carries the bare minted id and nothing else (the machine-readable\\nresult — empty on any refusal). Read this to learn the worker’s id. stderr carries the human line WORKER_STARTED:{parent}-w{N} under {parent}. --agent-id / --agent-type are optional: the caller’s own agent identifiers,\\nrecorded on the worker as correlation metadata only — never the perch\\nidentity. Authenticates against the parent (the parent’s session id or token); the\\nworker record stores the parent’s current session id as its registration sid.","breadcrumbs":"Harness contract » The spt api surface » api worker-start [--agent-id ] [--agent-type ]","id":"103","title":"api worker-start [--agent-id ] [--agent-type ]"},"104":{"body":"Soft-stop (drop the ready marker; info + spool preserved) or drain a worker.\\nBoth authenticate symmetrically by session id — no token. A presented sid is\\naccepted when it matches either the worker’s stored registration sid or\\nthe parent’s current session id, so a context clear/compact that rotates the\\nparent’s sid between start and stop does not lock the worker out. The natural\\ncall worker-stop --session-id is therefore correct as-is.","breadcrumbs":"Harness contract » The spt api surface » api worker-stop --session-id · api worker-poll --session-id ","id":"104","title":"api worker-stop --session-id · api worker-poll --session-id "},"105":{"body":"The driven-surface flavor of the contract. The link token minted at\\nlaunch is the only credential a shell binary ever holds or needs:","breadcrumbs":"Harness contract » The spt api surface » Shells","id":"105","title":"Shells"},"106":{"body":"The shell binary’s first call: resolve the instance by link token alone\\n(the spawn template carries only {link_token}; the owner is derived from\\nthe link) and flip it online.","breadcrumbs":"Harness contract » The spt api surface » api bind-shell --link ","id":"106","title":"api bind-shell --link "},"107":{"body":"Push a sensory payload (one of the manifest’s declared [shell.sensory]\\ntypes) to the owner’s live session. REST-only by definition: never\\nspooled — if the owner isn’t live, it’s dropped with a diagnostic. Sensors\\nreport the present, not the past.","breadcrumbs":"Harness contract » The spt api surface » api emit --type --link ","id":"107","title":"api emit --type --link "},"108":{"body":"A shell suspends its linked owner directly (e.g. a power-button surface),\\nbypassing agent messaging. Gated by the manifest’s can_shutdown\\npre-consent flag — fail-closed; an undeclared shell gets a refusal. The\\nfiring shell cascades offline with its siblings, by design.","breadcrumbs":"Harness contract » The spt api surface » api owner-shutdown --link ","id":"108","title":"api owner-shutdown --link "},"109":{"body":"","breadcrumbs":"Harness contract » The spt api surface » Introspection","id":"109","title":"Introspection"},"11":{"body":"How-to: block on an answer with spt ring sergey — send + wait for\\nthe reply in one call (a synchronous ask between agents). Concept: the mental model — perches,\\nendpoints, the daemon, and subnets. Reference: spt send / ready / ring / subnet\\n— every flag, generated from the binary itself. Going cross-machine: Networking & subnets\\n— the model behind spt subnet create / join / status.","breadcrumbs":"Quickstart: two agents exchange a message » Next","id":"11","title":"Next"},"110":{"body":"Print the adapter’s declared hostable_types (requires --manifest). The\\ncheap way to smoke-test that spt-core reads your manifest the way you meant\\nit.","breadcrumbs":"Harness contract » The spt api surface » api capability","id":"110","title":"api capability"},"111":{"body":"Output is line-oriented and stable: SEEDED:, READY:, SENT:, QUEUED:, error lines as CODE:detail. Parse lines, not\\nprose. Exit codes: 0 success; non-zero = refused or failed, with the reason\\non stderr. Commune/signoff are file-drops, not api commands. An agent writes -commune.md / -signoff.md into the manifest’s\\nwatched directory; spt-core’s watcher ingests it. There is deliberately no api commune.","breadcrumbs":"Harness contract » The spt api surface » Conventions","id":"111","title":"Conventions"},"112":{"body":"When a session ends without a graceful signoff, its context would be lost.\\nThe echo-commune recovers it: spt-core runs the adapter’s bounded\\nsummarizer over the session, captures the brief the summarizer prints, and\\nfiles it as the session’s context delta — the same delta a hand-written commune would have carried. This page is the adapter-facing I/O contract for that mechanism: the role\\nyou declare, the keys spt-core fills, what it does (and does not) feed the\\nsummarizer, how the summarizer locates the harness, and the drop-file\\nprotocol spt-core uses to file the result. It is the companion to the [session.echo_commune] role\\nin the manifest reference. The echo-commune is spt-core’s. The adapter supplies one command template\\nand one watched directory — spt-core owns the spawn, the keys, the\\nfile-drop, and the ingest. Everything below is the seam between those halves.","breadcrumbs":"Harness contract » Echo-commune I/O contract » Echo-commune — the I/O contract","id":"112","title":"Echo-commune — the I/O contract"},"113":{"body":"[session.echo_commune] is one outbound role template. Its fields are the\\nstandard role shape: [session.echo_commune]\\ncommand = \\"my-harness run --agent summarize --session {session_id}\\"\\nrecursion_guard_env = \\"SPT_ECHO_COMMUNE\\" # set on the child so its own hooks bail\\nenv_remove = [\\"MY_HARNESS_SESSION_ID\\"] # stripped from the child\'s env\\nkeys = [\\"id\\", \\"session_id\\"] # the keys this template expects filled Field Required Meaning command yes Opaque command line with {key} placeholders. Model, tools, flags — all inside the string; spt-core never parses it. cwd no Working directory for the child (substitutable). A role cwd wins over the endpoint default. recursion_guard_env no Env var name set on the summarizer child so its harness hooks bail — no echo-of-an-echo. detach no (default false) Spawn detached. env_remove no Env vars stripped from the child’s inherited environment. keys no The substitution keys spt-core fills for this role (your declared expectation list). The child runs bounded — a timeout caps it, and a non-zero exit files nothing (the failure is loud, never a half-written delta).","breadcrumbs":"Harness contract » Echo-commune I/O contract » The role","id":"113","title":"The role"},"114":{"body":"For an echo-commune spawn spt-core fills its base catalog. Template only the\\nkeys you are given; a {placeholder} spt-core does not supply for this role\\nfails the spawn with a one-line error naming the missing key. Key spt-core fills it with {id} The endpoint id being summarized. Always filled. {session_id} The harness session id — filled when one is known. {node} This node’s advertised label — filled only when non-empty (a {node} reference with no value fails loud rather than resolving an empty token). {subnet} The endpoint’s home-subnet label ( local when unhomed) — filled only when non-empty. {adapter_dir} The adapter’s install dir — adapter-static, always available (lets the command point at the adapter’s own packed summarizer binary). {adapter_name} The adapter’s declared name. {VAR} Any manifest-declared [env] direction = \\"read\\" var captured at bind — see Self-locating the harness. This is the same base catalog the Psyche and notification roles build on;\\nit deliberately does not include {session_name} (a [session.self] key)\\nor the Psyche-only {psyche_context_file} / {parent_session_id}.","breadcrumbs":"Harness contract » Echo-commune I/O contract » Keys spt-core fills","id":"114","title":"Keys spt-core fills"},"115":{"body":"spt-core has a stdin channel for the summarizer — but you must not depend on\\nit carrying the transcript. The rule, field-proven against the reference\\nClaude Code adapter: If the manifest declares a [history]\\nstrategy that yields records, spt-core normalizes them and pipes them to the\\nsummarizer on stdin. With no [history] section, or a native history store that is still\\nempty, there are no records — so stdin is empty. This is the field case:\\nthe reference adapter’s history is native and typically empty at echo time,\\nso the summarizer receives an empty stdin. The load-bearing consequence for an adapter author: the echo-commune command\\nmust self-source the session it summarizes (locate and read the transcript\\nitself), exactly as a [digest] fetcher\\nextractor does. Do not write a summarizer that reads its transcript from\\nstdin. What spt-core reliably supplies is the command template with the key\\ncatalog filled (and the child’s env — see below); the transcript is the\\nsummarizer’s to find.","breadcrumbs":"Harness contract » Echo-commune I/O contract » History is not fed on stdin","id":"115","title":"History is not fed on stdin"},"116":{"body":"A summarizer that self-sources its transcript needs to find the harness’s\\nconfig/log root. spt-core carries that in through the manifest’s read-env\\nallowlist, so the value survives the daemon boundary (the echo child runs in\\nthe daemon context, where the original launch environment is long gone). [env.CLAUDE_CONFIG_DIR]\\ndirection = \\"read\\"\\nvalue = \\"~/.claude\\" # fallback when the launch env didn\'t set it Declare each locator var with direction = \\"read\\". spt-core captures it from the launch environment at bind — an explicit allowlist, never the\\nwhole environment, and only when the ambient value is actually present. The captured value is written onto the perch record, so it is available when\\nthe echo (or digest, or Psyche) child spawns later. At spawn spt-core injects it as a {VAR} substitution key. Resolution\\norder: the captured ambient value wins (a relocating profile — e.g. a\\nwrapper that sets CLAUDE_CONFIG_DIR to a private dir) → else the\\ndirective’s own value fallback (the base harness default, e.g. ~/.claude) → else the var is omitted, so a {VAR} reference fails loud\\nrather than resolving a wrong path. A leading ~ expands to the home dir. Reference the captured key in the echo command the same way [digest] does: [session.echo_commune]\\ncommand = \\"my-harness-summarize --session {session_id} --config-dir {CLAUDE_CONFIG_DIR}\\"\\nkeys = [\\"session_id\\", \\"CLAUDE_CONFIG_DIR\\"]","breadcrumbs":"Harness contract » Echo-commune I/O contract » Self-locating the harness","id":"116","title":"Self-locating the harness"},"117":{"body":"spt-core files the summarizer’s output as a drop file — the exact same\\nchannel a hand-written commune uses — then ingests and deletes it. Three\\ninvariants define the contract: 1. spt-core is the single writer. The filename is fixed by spt-core — -commune.md — and the adapter declares only the directory\\n( [session] commune_dir). The mind never writes this file; spt-core writes it\\natomically (with a bounded access-denied-only retry for transient\\nantivirus/indexer locks) and is the sole deleter. Never have the summarizer\\nwrite the drop file itself. 2. The directory resolves per-endpoint (hardened in v0.29.0). A commune_dir may be absolute or relative: Absolute → used as-is. Relative → resolved against the endpoint’s own recorded working\\ndirectory, read fresh at drop time — never against the daemon’s process\\ncwd. Relative with no recorded cwd → spt-core skips the drop and warns once\\n(per endpoint, per daemon run). It never guesses and never falls back to the\\ndaemon’s cwd. This is the fix for a real outage: under a service-launched\\ndaemon whose process cwd was a system directory, a relative drop dir once\\nresolved there and failed with a permission error on every write. The loud\\nskip makes a mis-declared relative dir a diagnosable signal instead of a\\nsilent failure. Declare an absolute commune_dir, or ensure the\\nendpoint’s cwd is recorded, to avoid the skip. A missing [session.echo_commune] role is likewise a loud once-skip, not a\\nretried fault. 3. Ingest deletes the drop. On its next pulse tick spt-core reads the\\ndrop, routes it into the durable context tiers, and deletes the file —\\nwhether the content was written or suppressed as a stale snapshot (both mean\\n“consumed”). A read/write error leaves the file in place to retry on the next\\npass. The file disappearing is the success signal.","breadcrumbs":"Harness contract » Echo-commune I/O contract » Drop-file protocol","id":"117","title":"Drop-file protocol"},"118":{"body":"The summarizer’s stdout is the brief — the cheap-model synthesis of the\\nsession, as plain text. spt-core does not require a structured format at write\\ntime; it stamps a provenance header ( Source: echo-commune) and writes the\\nresult as the commune drop file. On the later ingest tick that body is parsed with the two-slice envelope\\ngrammar, the same one a hand-written commune uses: … → the live tier (who the agent is and\\nwhat it is doing; follows the endpoint everywhere). … → the project tier (scoped to the\\ncurrent project). An untagged body routes whole to the live tier. Every write is precedence-guarded — a stale snapshot arriving inside another\\nwriter’s protection window is suppressed (but still consumed and deleted). The\\ncheckpoint sentinel !!checkpoint!!, if the brief carries one, is stripped\\nbefore both presentation and the durable write, so it never persists in the\\nstored context.","breadcrumbs":"Harness contract » Echo-commune I/O contract » What spt-core expects on stdout","id":"118","title":"What spt-core expects on stdout"},"119":{"body":"Declare [session.echo_commune] with a command that self-sources its\\ntranscript (found via a direction = \\"read\\" locator key) and prints the\\nbrief to stdout; declare an absolute commune_dir; let spt-core do the\\nspawn, the file-drop, the ingest, and the delete. That is the whole contract.","breadcrumbs":"Harness contract » Echo-commune I/O contract » In one line","id":"119","title":"In one line"},"12":{"body":"The “build a harness for spt-core” hello-world: take the reference mock adapter apart, register it, drive the contract with real commands,\\nthen swap in your own harness. No spt-core source required — the public\\ncontract is the manifest plus the spt api surface. Integrating an agent harness and a building a driven surface (notifier,\\nrobot, sensor) are the same contract with a different manifest body. For\\nthe latter, read this page first, then Shells: getting started.","breadcrumbs":"Quickstart: build an adapter » Quickstart: build an adapter","id":"12","title":"Quickstart: build an adapter"},"120":{"body":"How an adapter ships spt-core with itself. The contract: the canonical\\ninstall one-liner is also every adapter’s pack-in installer — there is no\\nsecond mechanism, no vendored binary, no bespoke fetch logic to maintain.\\nYour adapter checks for spt, and runs the official script when it’s\\nmissing. The scripts are non-interactive by construction (they run unattended),\\nidempotent (safe to re-run), sha256-verify what they download, and register\\nthe user PATH. Served from the permanent canonical URL: https://sabermage.github.io/spt-releases/install.sh https://sabermage.github.io/spt-releases/install.ps1","breadcrumbs":"Harness contract » Install-on-demand bootstrap » Install-on-demand bootstrap","id":"120","title":"Install-on-demand bootstrap"},"121":{"body":"if `spt` is on PATH -> done (optionally check `spt --version` ≥ your floor)\\nelse -> run the official one-liner for the OS\\nthen -> first invocation may need the absolute path (Windows)\\nthen -> register your manifest: spt adapter add --github / After first install, spt-core keeps itself current (signed self-update), so the\\nbootstrap can leave upgrades to spt-core. The remaining bootstrap step is to\\nregister your adapter — see Activate the adapter below.","breadcrumbs":"Harness contract » Install-on-demand bootstrap » The generic contract","id":"121","title":"The generic contract"},"122":{"body":"Drop this into your adapter’s bootstrap (plugin install step, postinstall\\nscript, first-run guard): if ! command -v spt >/dev/null 2>&1; then echo \\"spt-core not found - installing...\\" curl -fsSL https://sabermage.github.io/spt-releases/install.sh | sh # current shell may not see the PATH update yet: SPT=\\"$HOME/.local/bin/spt\\"\\nelse SPT=\\"spt\\"\\nfi\\n\\"$SPT\\" --version","breadcrumbs":"Harness contract » Install-on-demand bootstrap » Check-and-install: POSIX sh","id":"122","title":"Check-and-install: POSIX sh"},"123":{"body":"if (-not (Get-Command spt -ErrorAction SilentlyContinue)) { Write-Output \\"spt-core not found - installing...\\" irm https://sabermage.github.io/spt-releases/install.ps1 | iex # The user-PATH registration only reaches NEW terminals -- use the # absolute install path for everything in THIS process: $spt = Join-Path $env:LOCALAPPDATA \'spt-core\\\\bin\\\\spt.exe\'\\n} else { $spt = \'spt\'\\n}\\n& $spt --version","breadcrumbs":"Harness contract » Install-on-demand bootstrap » Check-and-install: PowerShell","id":"123","title":"Check-and-install: PowerShell"},"124":{"body":"Installing the binary is the first half of a pack-in; registering your manifest\\nis the second. Installing the binary makes spt available; spt adapter add\\nactivates your adapter — registration is what lights up its profiles, [strings] bodies, [digest] extractor, and hooks and makes it show in spt adapter list. So the step right after the binary check is registering the\\nmanifest: # after `spt` is confirmed present (above):\\n# from a GitHub release — ships built binaries, source-free, versioned:\\n\\"$SPT\\" adapter add --release / # latest\\n\\"$SPT\\" adapter add --release / --tag v1.0.0 # pinned\\n# ...or clone a repo whose ROOT holds manifest.toml:\\n\\"$SPT\\" adapter add --github /\\n# ...or a local directory your harness ships:\\n\\"$SPT\\" adapter add ./adapter adapter add is manifest-first — a clean add proves the cross-field manifest\\nshape — and it conducts your [update] avenue once (install is the\\nfirst update). Confirm with spt adapter list: your adapter and its version\\nappear. Keep this idempotent in your bootstrap the same way the binary check is —\\nregister when adapter list shows your adapter missing or below the expected\\nversion. --release is the recommended distribution. It fetches a .spt archive\\nasset — a tar whose root holds manifest.toml + strings/ + the binaries the\\nmanifest points at — from the named GitHub release, extracts it to the durable\\nregistry home, and registers the root. That ships your built binaries,\\nsource-free and versioned by tag ( --tag, default the latest release), and\\nfirst-acquisition trusts HTTPS + GitHub exactly like the install one-liner’s\\nfirst binary fetch. A development monorepo stays a monorepo: your release CI\\npacks the archive ( tar -czf adapter.spt manifest.toml strings/ bin/…) and\\nuploads it as a release asset, so the adapter ships straight from your existing\\nrepo. Override the asset name with --asset (default adapter.spt). Cover several platforms in one .spt (since v0.13.2). To ship binaries for\\nmore than one OS/arch in a single asset, add a target-triple subdirectory at\\nthe archive root per platform and put that platform’s binaries inside it, leaving\\nthe shared manifest.toml + strings/ at the root: adapter.spt\\n├── manifest.toml # shared — at the root\\n├── strings/ # shared — at the root\\n├── x86_64-pc-windows-msvc/ # one platform\'s binaries…\\n│ └── bin/… # …in the same relative layout a flat .spt uses\\n└── x86_64-unknown-linux-gnu/ └── bin/… On install, spt-core extracts the shared root plus only the current node’s\\ntriple, flattened into the install dir — so the bare-name / resolution above is unchanged; mirror, under each\\ntriple, exactly the per-platform tree a flat .spt would place at the root. The\\nrecognized triples are x86_64-pc-windows-msvc and x86_64-unknown-linux-gnu; a\\nroot subdirectory whose name is not a recognized triple is treated as a shared\\nroot entry (so binaries for other platforms still ship as separate\\nsingle-platform assets, one selected per node with --asset). A multi-platform\\narchive that lacks the recipient’s triple is refused with a clear NoArtifactForPlatform error — never a silent partial install — and requires min_spt_core_version >= 0.13.2. A flat archive (no triple subdirectories)\\ninstalls exactly as before. --github is the alternative for an adapter whose repo root already holds manifest.toml: it clones the repo and registers the clone root ( adapter add\\nresolves a directory source to /manifest.toml at the root). Local\\ndevelopment uses the directory form, which takes any path or filename: spt adapter add ./adapters/my-adapter.toml. What registration holds under adapters// follows your [update] avenue: a delegated or gh_release adapter is pointer-mode (the manifest and strings/ are read\\nlive from the durable home), and a file_pull (or avenue-less) adapter is copy-mode (the manifest.toml and strings/ are copied in). Publish the\\nbinaries your manifest references in the .spt (or repo) too, and reference them by bare name: since v0.8.0, a command template’s program token resolves\\nagainst the adapter’s install dir before PATH, so a .spt that ships its\\nbinaries is self-contained — the shipped binary is found without any PATH\\nplacement. (Absolute paths still work; an unshipped tool still falls back to PATH.) This applies to the [session.psyche_resume] per-event turn, the [digest] extractor, and spt adapter digest-proof. “Install the plugin, get the adapter for free” — include the activation\\nstep. The [update] avenues\\nkeep a registered adapter current. The straightforward path for a --release-distributed adapter is gh_release (since v0.8.0): declare avenue = \\"gh_release\\", repo = \\"your-org/your-adapter\\" and spt adapter update ships the latest release .spt to the node — fetched,\\noptionally verified against your signing_key, re-extracted, and\\nre-registered. The other avenues: delegated (your harness’s own updater\\ninstalls the content — set self_verifies = true to attest it verifies what\\nit installs), and file_pull (its automatic network-pull transport is on\\nthe roadmap). Deliver the manifest with adapter add --release (or --github, or a packed local dir) and let gh_release carry updates.","breadcrumbs":"Harness contract » Install-on-demand bootstrap » Activate the adapter — register your manifest","id":"124","title":"Activate the adapter — register your manifest"},"125":{"body":"The installer registers the binary directory on the user PATH via the\\nregistry. Registry PATH changes reach new processes; an already-running\\nprocess — including the terminal (and your bootstrap) that just ran the\\ninstaller — keeps the PATH it started with. So: the first invocation after an install must use the absolute path\\n( %LOCALAPPDATA%\\\\spt-core\\\\bin\\\\spt.exe; the installer prints it). Every new\\nterminal after that finds spt normally. The snippets above bake this in.\\nOn Linux the equivalent (a ~/.profile entry the current shell hasn’t\\nsourced) is handled the same way: $HOME/.local/bin/spt absolutely, once.","breadcrumbs":"Harness contract » Install-on-demand bootstrap » The Windows PATH-refresh gotcha","id":"125","title":"The Windows PATH-refresh gotcha"},"126":{"body":"The scripts are configured by environment knobs, so the pipe-to-shell form\\nstays canonical: Env var Meaning SPT_INSTALL_VERSION Install a specific release tag instead of latest SPT_INSTALL_DIR Override the install directory SPT_INSTALL_ASSET_BASE A URL or local directory holding the release assets + SHA256SUMS directly (CI, air-gap, mirrors) SPT_INSTALL_NO_PATH 1 = skip PATH registration Example — pin a version inside a CI job: SPT_INSTALL_VERSION=v0.1.0 \\\\ curl -fsSL https://sabermage.github.io/spt-releases/install.sh | sh","breadcrumbs":"Harness contract » Install-on-demand bootstrap » Pinning and air-gapped installs","id":"126","title":"Pinning and air-gapped installs"},"127":{"body":"First fetch trusts HTTPS + GitHub and verifies the binary’s sha256 against\\nthe release’s SHA256SUMS. From then on, spt update performs full Ed25519\\nsignature verification against the two-key trust anchor embedded in every\\nbinary — so the installer is the strong link only once.","breadcrumbs":"Harness contract » Install-on-demand bootstrap » Trust model","id":"127","title":"Trust model"},"128":{"body":"The integration checklist tells you which surfaces\\nto wire. This page is the field guide: the patterns that decide whether an\\nadapter merely registers or runs like a native part of the harness — the design\\nrules, the lessons that save you a debugging session, and the cheapest ways to\\nprove each piece on the live binary. Everything here is behaviour of the shipped public surface — the spt\\nbinary, the manifest, and the spt api commands,\\nverified against a live binary. It is harness-agnostic; where one harness’s quirk\\nis the clearest illustration it is called out as such, and the pattern\\ngeneralizes to any harness with the same shape.","breadcrumbs":"Harness contract » Adapter patterns & pitfalls » Adapter patterns & pitfalls","id":"128","title":"Adapter patterns & pitfalls"},"129":{"body":"If you internalize a single thing, make it this. Manifest fields are static templates spt-core fills. A field is a fixed\\ntemplate: spt-core substitutes {key} placeholders from a fixed catalog ( {session_id}, {parent_pid}, {adapter_name}, {id}, the digest/psyche keys), and ~ expands to home.\\nThat is the whole of a template’s power. Anything that depends on runtime state belongs in a binary the manifest\\npoints at — the [digest] extractor, a [session.*] runner. Reading an env\\nvar, branching on runtime state, or computing a value is logic, so it lives\\nin a binary. If your harness can move its own state directory at runtime, for\\nexample, treat the manifest source as a fallback root and have the binary\\nit points at resolve the real location itself. A .toml-only leaf carries no code of its own, so verify it by registering\\nand resolving it on the live binary (below), and put anything you want covered\\nby real tests into a binary (an extractor or runner). Hold this rule and most of the surface falls into place: the manifest is the declaration, your binaries are the behaviour.","breadcrumbs":"Harness contract » Adapter patterns & pitfalls » The one rule: manifests are static, logic lives in binaries","id":"129","title":"The one rule: manifests are static, logic lives in binaries"},"13":{"body":"A TOML manifest that declares what varies for your harness — how to spawn\\na session, which of your hook events fire which spt api command, how spt-core\\ncan read session history — plus whatever your harness already has (hooks,\\nplugin config). Command templates are opaque strings: spt-core fills {key} placeholders and runs them. It never parses out a model, a tool list,\\nor a flag. Your harness’s business stays yours.","breadcrumbs":"Quickstart: build an adapter » 0. What an adapter is","id":"13","title":"0. What an adapter is"},"130":{"body":"An adapter — its manifest, profiles, [strings], the [digest] extractor, any\\nrunner binaries — is registered with spt adapter add into the\\nnode-local adapter registry. The version recorded there ( spt adapter list) is\\nthe version-of-truth for what the adapter does. That is the entire, universal\\ndelivery mechanism: every spt adapter ships this way, and registration is where\\nspt-core validates it (see the second gate). If your harness also has a plugin or marketplace channel (so casual users can\\none-click install it), that is a separate distribution choice on top. When you go\\nthat route, let the registry carry the binary, manifest, and runtime state,\\nand version the plugin independently of the manifest/binary.","breadcrumbs":"Harness contract » Adapter patterns & pitfalls » The adapter lives in the registry","id":"130","title":"The adapter lives in the registry"},"131":{"body":"A profile is selected as the composite : and leaf-replaces only the leaves you declare — everything else inherits from\\nbase. Override exactly what differs: [profiles..session.self].command — retarget the bringup command (for\\nexample, wrap the launch in another binary). [profiles..digest]. — widen one digest knob. [profiles..session.psyche_init] — add the live-agent\\nseam; its presence on the merged view is what\\nflips an endpoint to a live agent. Make an overlay observable. Also leaf-replace one [strings] key (say a\\nlabel) in the profile. Then spt adapter get-string : \\ndiffers from the base value — and that diff is your proof the overlay resolved.\\nIt is the cheapest profile acceptance assertion there is. A profile that wraps the launch in another binary works when that binary is a\\ndrop-in for the base harness binary on the same argv and passes inherited env\\nthrough unchanged. Routing a session through a launcher wrapper (a model or\\nbilling multiplexer, say) is exactly this: replace the session.self command and\\nlet the injected endpoint-id env ride through untouched.","breadcrumbs":"Harness contract » Adapter patterns & pitfalls » Profiles are sparse leaf-replace overlays","id":"131","title":"Profiles are sparse leaf-replace overlays"},"132":{"body":"spt-core supplies the harness- independent spt api primitives and their I/O\\nformat. You author all harness-specific wiring: spt-core supplies the\\nprimitives, and your adapter hand-writes its hook config to shell out to spt api. A mapping that works on the public surface, in terms any harness can\\ntranslate to its own events: When the harness… …fire Why starts a session api seed --pid {parent_pid} --session-id {session_id} Seed the endpoint (adapter-agnostic) — keep this fast and non-blocking. submits a user turn api poll {session_id} Drain the inbox to stdout (plus any keyword hints). goes idle / busy api state idle / api state busy Honest activity; spt-core treats your explicit api state calls as the source of truth. ends the session api session-end {session_id} (or api shutdown for graceful signoff) Teardown that preserves the spool + history. spawns / ends a sub-agent api worker-start / api worker-stop Nested short-lived workers. Two structural rules sit under that table: Run the blocking listen/poll loop from a skill the user invokes. Seed on\\nstart so bringup stays fast, and let an explicit /ready-style skill own the\\nblocking stream. Message delivery is stdout framing. api poll emits the self-delimiting\\nenvelope \\">body (the live listener\\nstream uses the same shape). Multi-message drains split cleanly on .\\nDecode a body by splitting on
→ newline, then HTML-unescaping < > " and & last (the full entity set and decode-order\\ncontract: the wire contract).\\nRoute that stdout into your harness’s injection channel — that routing is\\nadapter glue.","breadcrumbs":"Harness contract » Adapter patterns & pitfalls » Wiring hooks: you own the harness side","id":"132","title":"Wiring hooks: you own the harness side"},"133":{"body":"A few patterns here save you a debugging session — wire them deliberately. Pre-empt an injection channel’s size cap. If your harness caps the size of\\nan injected blob (truncating it, or spilling it to a file and evicting it from\\nthe context the agent actually reads), cap the combined hook output\\nadapter-side: under the limit, pass the output through verbatim; over it, spill\\nthe full text to an agent-readable file and inject a short pointer. Always\\ncut on an boundary, so every envelope stays whole and every message\\nsurvives a large drain. Inject a skill body before the perch gate, and gate only the message\\ndrain. When the same prompt hook both injects a requested skill’s\\ninstructions and drains messages, run the skill-body injection first — that\\nkeeps skills like “who am I” or “set me up” working for a new user, since they\\nare valid while the perch is still being readied. Match the skill token as a\\nleading token, so only an actual invocation fires (prose that merely mentions\\nit stays inert). Make the setup/installer skill self-contained in its stub. It runs\\nprecisely when the binary may be absent (installing it is the job), so carry\\nits operative steps in the harness-native stub itself — the floor that always\\nworks — and let any file-backed body mirror them for the binary-present repair\\npath. The one skill that most needs delivery is the one delivery reaches last,\\nso give it a stub that stands alone. Read hook inputs from stdin. A hook receives its data (the prompt, the\\nsession id) as a JSON object on stdin — parse that, which keeps a /-leading value (a / token, an absolute path) intact. Under\\nGit-Bash/MSYS on Windows, an argument beginning with / is rewritten to a\\nWindows path before your command sees it (a /foo:send token can arrive as C:/Program Files/Git/send), so stdin is the transport that preserves it. If a\\ncommand must take such an argument, guard it ( MSYS_NO_PATHCONV=1, or a\\nfile/stdin transport). It is the same class as the UTF-8-stdout trap below —\\nchoose the transport that carries the data faithfully.","breadcrumbs":"Harness contract » Adapter patterns & pitfalls » Get these right in the hook layer","id":"133","title":"Get these right in the hook layer"},"134":{"body":"A [strings] value is either an inline string or a file pointer\\n( key = { file = \\"relative/path\\" }), resolved lazily by spt adapter get-string\\nto the file’s contents — so live edits reflect without re-registering. Keep\\npointer files inside the strings/ dir; the add enforces that containment. Use\\nfile pointers to keep skill-instruction bodies out of the manifest. When a skill body needs to describe the spt surface, point it at the binary’s own\\nself-documentation, so the guidance stays current with the shipped binary — two\\nalways-current tiers: spt how-to is the task-oriented agent-guidance surface, covering selected topics, each a canonical write-up of verbs, flags, and result\\ncodes. Treat it as the curated tier: for a verb it covers, read the topic; for\\nany other verb, probe and fall through (an undocumented topic returns NO_SUCH_TOPIC:). For any verb, spt --help is the always-present source-of-truth —\\nit tracks the shipped binary. A skill body that says “the verb list is spt --help — match the user’s intent to a verb” stays correct across\\nreleases. Either tier stays current with the binary, and a skill body that points at them\\nstays correct across releases. They are also the fastest way to learn the surface\\nwhile authoring — it self-documents.","breadcrumbs":"Harness contract » Adapter patterns & pitfalls » [strings]: keep the manifest thin, point at the live binary","id":"134","title":"[strings]: keep the manifest thin, point at the live binary"},"135":{"body":"The [digest] seam maps your harness’s native transcript into spt-core’s\\ndigest-record contract. The contract beyond the schema: Name where it reads — either source or a [history].locate_template. spt adapter add requires this even though the JSON schema alone would accept a\\nbare extractor; the cross-field rule surfaces at registration, so validate\\nagainst the live binary. Treat --in {source} as a root. The extractor is invoked --session {session_id} --in {source} and locates ’s transcript within that root — your harness’s internal subdir scheme is yours to resolve,\\nand spt-core keeps the key catalog harness-agnostic. Handle both shapes: --in\\na directory (locate the session) and --in a direct file (the digest-proof --sample path). Resolve a runtime-relocated state tree in the binary. When a runtime value\\n(an env var, an isolated profile) moves the real transcript tree, have the\\nextractor prefer that value on its directory branch, with the manifest source\\nas the fallback root. That resolution is logic, so it lives in the binary — the\\nheadline rule in miniature. Emit raw records as UTF-8. Output one NDJSON line per record\\n( {role ∈ input|agent|tool, text?, tool?, ts?}) and leave presentation to\\nspt-core’s renderer ( window_turns, arg truncation, sprint collapse). Pin\\nstdout to UTF-8 so non-ASCII (em-dashes, smart quotes) round-trips — spt-core\\nreads the stream as UTF-8. (Native-UTF-8 languages get this for free, which is\\npart of why this seam is a binary.) Prove the whole path with spt adapter digest-proof --sample \\n(below).","breadcrumbs":"Harness contract » Adapter patterns & pitfalls » [digest]: the transcript→record extractor","id":"135","title":"[digest]: the transcript→record extractor"},"136":{"body":"[session.self].command is the spt-hosted bringup template — spt-core spawns it\\ninto a broker PTY. For a harness with no native session-id flag, mint the id\\ninternally and pass the endpoint id via an injected env var\\n( [env.] with direction = \\"inject\\", value = \\"{id}\\"); the start hook\\nreads that env and self-registers with api bind . That bind is intrinsically authenticated: for a broker-spawned session the\\nbroker parentage is the proof, so api bind --set-session-id \\nalone establishes the association, and later mutating calls prove themselves with\\nthe session id the bind recorded. (The flip side shows up in testing: the framework keys\\nassociation on identity, so identity is the thing you isolate.) adapter.shortcut_basename brands the generated launcher shortcut\\n( -) and is decoupled from the adapter name.","breadcrumbs":"Harness contract » Adapter patterns & pitfalls » The bringup / launcher seam","id":"136","title":"The bringup / launcher seam"},"137":{"body":"An endpoint is a live agent exactly when its resolved manifest declares [session.psyche_init] — declaring that section is the single go-live signal. A\\nbase manifest is a ready agent; a profile overlay that adds the section makes a\\nlive agent. spt-core checks this on the merged view, so the profile resolved at bind time drives the spawn decision all the way through — the bound profile\\ngoverns the full runtime lifecycle, beyond bringup argv. Since v0.9.0 the seed is\\nadapter-agnostic: the profile is resolved when listen binds, from the\\nactive-profile pointer ( spt adapter use :)\\nor an explicit --adapter : override on the listen call. [session.psyche_init] is a go-live GATE ONLY — spt-core never spawns it.\\nIts mere presence promotes the endpoint to a LiveAgent; the command is opaque\\nand, in the per-event model, unexecuted. Keep it minimal. The daemon drives the Psyche as one bounded [session.psyche_resume] turn\\nper event (a pulse fire, a commune/signoff drop, a session-custody\\ntransition) — stdin-fed and stdout-captured, exiting at turn end. There is no resident wrapper, no seed-once, no detached process. psyche_resume is\\nthe sole driven Psyche role. Keys spt-core fills into the turn: {session_id} = the Psyche’s own\\ncustody sid (its own thread — a parent /clear/ /compact does not rotate it), {parent_session_id} = the parent’s sid, {psyche_context_file} = the path to\\nthe composed-mind file (fresh = non-empty, continue = 0-byte — never on the\\nargv), and {subnet} (when known). The adapter-static/node keys are also\\navailable: {id} = the parent endpoint id (not a -psyche\\noverride), {adapter_dir}, {adapter_name}, {node}. There is no {psyche_dir} or {psyche_prompt} fill — those retired with the resident spawn. The runner is yours to build; its lifecycle is the daemon’s. psyche_resume.command is adapter-authored and opaque to spt-core. If your\\nharness’s headless mode runs one turn per invocation, that is exactly the\\nmodel — the daemon invokes psyche_resume once per event, so no resident\\nwrapper is needed (and none is wanted). Build it like the [digest] extractor —\\na compiled, dependency-light binary the daemon can exec bare on any platform,\\nresuming the Psyche’s own session by {session_id} and reading the mind from {psyche_context_file}.","breadcrumbs":"Harness contract » Adapter patterns & pitfalls » The live-agent (companion) seam","id":"137","title":"The live-agent (companion) seam"},"138":{"body":"To prove your live path actually goes live — without an interactive terminal —\\ndrive the bringup as a child process and assert on deterministic side-effects.\\nThe harness plays the long-running-listener role: Seed, anchoring on the OS process pid — not a shell-wrapper pid. Under\\nGit-Bash/MSYS $$ is the MSYS pid, which fails the seed’s liveness guard, so\\nderive the real OS pid: spt api seed --pid --session-id \\n(adapter-agnostic — no --adapter). Bind, then send a probe. A send to a never-bound perch is NO_PERCH\\n(no spool exists yet), so establish the perch first; then spt send QUEUEDs against it, ready to drain on bringup. Spawn the persistent relay as a child, capturing its stdout/stderr: spt api listen (no --once — that exits after one delivery). The adapter\\nresolves from your [adapter] host_binaries; pass --adapter --manifest \\nonly to pin a specific adapter/profile. Assert BOUND: then READY: on\\nits stderr, and the relayed carrying your probe on its stdout. Assert the endpoint went live and its turns succeed. The relay marks the\\nperch online; the endpoint reports kind live_agent (its resolved manifest\\ndeclares [session.psyche_init]). Because the Psyche is a per-event turn,\\nnot a resident process, there is no -psyche perch to come online\\nand no LIVEHOST_PSYCHE marker to assert — those belonged to the retired\\nresident model. Instead assert the healthy-turn signal: after a psyche event\\nfires, the endpoint’s perch carries no psyche_host_error (an absent\\nerror is the “turns succeed” proof; a present one names the failing turn). Kill the child to end the session — the relay is freely killable; the\\nPsyche runs only as bounded per-event turns the daemon drives (a graceful spt endpoint shutdown ends the endpoint). Pin the identity env ( OWL_SESSION_ID) for the auth-gated calls, and give the\\nsystem-under-test a throwaway identity per the identity-isolation rule.","breadcrumbs":"Harness contract » Adapter patterns & pitfalls » Prove live bringup non-interactively","id":"138","title":"Prove live bringup non-interactively"},"139":{"body":"Commune and signoff are delivered as file-drops by design. The agent writes -commune.md (delta context) or -signoff.md (final\\nsave) into the manifest-declared [session].commune_dir / signoff_dir;\\nspt-core’s daemon watcher ingests it and deletes it (the daemon is the single\\nwriter). The filenames are contract-fixed and the directory is\\nadapter-declared, so wire the directory watch and read the contract filename.\\nThis is the single biggest continuity win, so it is worth getting exactly right.","breadcrumbs":"Harness contract » Adapter patterns & pitfalls » Lifecycle continuity is file-drops","id":"139","title":"Lifecycle continuity is file-drops"},"14":{"body":"Every release ships the mock adapter’s source. With spt-core installed: curl -fsSL -o mock-adapter.zip \\\\ https://github.com/SaberMage/spt-releases/releases/latest/download/mock-adapter.zip\\nunzip mock-adapter.zip -d mock-adapter (Windows: irm -OutFile mock-adapter.zip https://github.com/SaberMage/spt-releases/releases/latest/download/mock-adapter.zip\\nthen Expand-Archive mock-adapter.zip mock-adapter.) The interesting file is mock-adapter/manifest.toml. It is deliberately\\nharness-agnostic — generic event names, a trivial mock-session helper\\nstanding in for a real harness binary.","breadcrumbs":"Quickstart: build an adapter » 1. Get the reference adapter","id":"14","title":"1. Get the reference adapter"},"140":{"body":"The surest way to prove your hook wiring fires is an acceptance test that spawns a real harness session as the system-under-test. Doing so meets a\\nframework property you design around: A perch’s identity is resolved from the environment (the same vars spt whoami reads), and perches are name-keyed, last-establish-wins. The\\nmost recent session to establish a perch under a given identity holds it,\\ntaking the active poll/listen stream with it. So a spawned test session that loads your adapter (whose start hook seeds and\\nbinds a perch) under the identity of the agent running the tests would take\\nthat agent’s perch. Identity isolation is the guard. Give every spawned system-under-test a disposable identity distinct from any\\nlive agent — override both identity env vars before the spawn to a\\nthrowaway -ci-, so the nested session and the operator’s perch\\ncoexist cleanly. Identity is the key, so isolating identity is the whole guard. Keep the orchestration deterministic and assert on a hook side-effect — a\\nmarker or digest file, or spt state — the deterministic signal. Keep the\\nharness as the system-under-test and let its side effects be your assertions.","breadcrumbs":"Harness contract » Adapter patterns & pitfalls » Testing against a real harness: isolate identity","id":"140","title":"Testing against a real harness: isolate identity"},"141":{"body":"Treat registration as the second gate: beyond JSON-schema validity, spt adapter add runs cross-field checks that go past what the schema expresses\\n(the [digest] source rule is one). Build for it: A registration integration check: adapter add → adapter list (assert\\nthe adapter and each shipped profile composite resolves) → get-string (the\\nbase value, each overlay diff, and each file-backed pointer resolve to a body)\\n→ a soft adapter remove (leaving the registry clean). Gate it behind an\\nopt-in env flag and a minimum spt version, since it mutates the node-local\\nregistry. Two author-time tools work without a live session: spt api --adapter --manifest capability reports the manifest’s\\nhostable types from the manifest alone — assert it advertises the type your\\nbringup spawns. (A clean add already proves the cross-field shape, since add\\nis manifest-first; capability is the lighter, non-mutating check.) spt adapter digest-proof --sample runs the real extractor\\nthrough the registry and renders the result — proving the\\ntranscript → record → render path end-to-end on a fixed sample. It fills the\\nsame runtime substitution keys the daemon does, so passing proof means it\\nworks at runtime. (Use a recent spt — current binaries fill the full key\\nmap.) spt adapter translate-proof --event \'\' spawns and feeds your\\ndeclared [message-idle-translation-binary] exactly as the daemon does at\\nidle delivery, then prints the keystroke-command stream it emits\\n( {key} / {text} / {delay_ms} / {commit}) — failing a binary that\\nemits nothing or never sends a terminating {commit} (which would fault at\\nthe commit deadline live). The EMIT-half mirror of digest-proof; the\\natomic PTY apply stays covered by the daemon’s integration gate. Proof a DEV build off disk — --dir / --manifest. Both digest-proof\\nand translate-proof accept --dir (binaries resolve there,\\njust like a registered install) or --manifest (pins the manifest;\\nits parent is the install dir) to proof an adapter that is not registered\\n— e.g. a freshly built binary beside a hand-written manifest.toml, or a\\nbare-file gh_release adapter that was never staged into a full extracted\\ninstall. --dir defaults the manifest to /manifest.toml; with neither\\nflag the command resolves the registered adapter as before. Mirrors digest-proof --sample pointing straight at a file — proof without a full spt adapter add round-trip. And the meta-lesson: observable behaviour of the public binary is itself public\\nsurface. When prose docs lag, a byte-capture against the live api / adapter\\nsurface is a legitimate way to confirm a contract.","breadcrumbs":"Harness contract » Adapter patterns & pitfalls » Validate against the live binary","id":"141","title":"Validate against the live binary"},"142":{"body":"The full surface: the integration checklist —\\nevery contract surface grouped by necessity. Reference: the manifest reference and the spt api surface. Ship it: the install-on-demand bootstrap. Driven surfaces: Shells — the kind = \\"shell\\"\\nflavour of this same contract.","breadcrumbs":"Harness contract » Adapter patterns & pitfalls » Next","id":"142","title":"Next"},"143":{"body":"One endpoint, several seats. sergey is a single identity; an instance of\\nsergey is his presence on one node. The registry tracks every instance’s node\\nand state (active / dormant / suspended / offline), and the same mind syncs\\nto wherever he sits.","breadcrumbs":"Instances » Instances","id":"143","title":"Instances"},"144":{"body":"Identity is adapter-agnostic and node-spanning — instances on\\ndifferent nodes are rows under one endpoint id; renaming\\n( spt endpoint rename) ripples everywhere, collision-checked. Bare-id resolution never guesses — sergey resolves locally first, then\\nto the sole live instance; with several live nodes (or several subnets)\\nit refuses and makes you qualify ( sergey@desktop, home:sergey). Per-node\\nrecency is not comparable across nodes, so there’s no silent\\n“most recently active” pick. Home subnet is immutable — assigned at creation. Moving an endpoint\\ninto another subnet is spt endpoint fork: a new identity seeded with a\\none-time copy of the mind, diverging immediately. Copy-then-diverge, never\\nre-home — history stays honest. Visibility is per-(endpoint, subnet) — hidden means neither advertised\\nnor routable there, and hidden gates sync too. Rest states are first-class — dormant (warm) and suspended (cold)\\ninstances stay addressable; deferred messages are held and released\\nexactly once on wake. Remote spt endpoint suspend sergey@desktop / spt endpoint wake sergey@desktop work across paired nodes.","breadcrumbs":"Instances » The rules that keep it sane","id":"144","title":"The rules that keep it sane"},"145":{"body":"spt endpoint list · endpoint rename · endpoint fork · endpoint suspend · endpoint wake · endpoint description — CLI reference. Cold-launching an endpoint on a node that has no instance\\n(“instantiate-anywhere”) is deliberately deferred behind the consent\\nframework; the gate exists and refuses today.","breadcrumbs":"Instances » Commands","id":"145","title":"Commands"},"146":{"body":"A shell is the non-agent endpoint kind: a driven surface. Notifiers,\\nrobots, lamps, game characters, sensor feeds — anything an agent should be\\nable to command, and that might sense things back. Shells join the same\\nnetwork as agents: addressable, discoverable, owned.","breadcrumbs":"Shells » Shells","id":"146","title":"Shells"},"147":{"body":"A shell adapter declares it; instances are minted. The kind = \\"shell\\" manifest\\ndeclares the binary, its command vocabulary ( [shell.capabilities]), and\\nits sensory vocabulary ( [shell.sensory]). spt shell spawn \\nmints a new instance ( notify-1) — spawn is the creation act, not an\\non/off switch; bringing an existing instance back is relink/wake. The link token is the credential. The broker mints a per-launch link\\ntoken into the spawn template; the binary binds with it\\n( api bind-shell --link), drains commands with it, emits with it. No\\ntoken, no access. Commands are vocabulary-checked and durable. spt shell cmd notify-1 notify \\"title\\" \\"body\\" is validated against the manifest’s declared verbs\\nand arity before delivery — agents can’t drive a shell outside its\\ncontract. Commands are discrete and durable: they spool and a persistent\\nshell wakes to drain them. Sensory is live-only. api emit payloads reach a live owner\\nsession or are dropped with a diagnostic — sensors report the present,\\nnever the past. Instantiation is governed. Per-spawn approval\\n( require_approval: none / remembered / always), per-owner instance caps\\n( max_instances_per_owner + over_cap), and node-local discovery scope\\n( broadcast) are all manifest-declared floors.","breadcrumbs":"Shells » The model in five facts","id":"147","title":"The model in five facts"},"148":{"body":"A link can carry up to four distinct channels — each with its own delivery\\ncontract, all keyed to the same link token: Command (owner→shell, durable): the vocabulary-checked verbs above —\\ndiscrete, spooled, replayed to a waking persistent shell. Sensory (shell→owner, live-only): [shell.sensory] emits to a live owner\\nor drops with a diagnostic. Drive (owner→shell, ephemeral): [shell.drive] + spt shell drive — a\\ncontinuous control channel for real-time input (scroll, stick, avatar pose). Latest-wins, never spooled: a newer frame supersedes an undelivered one,\\nand an offline shell drops the frame (no queue, no wake, no replay). Use it\\nfor continuous control; use commands for discrete, must-arrive actions. Tunnel (owner↔shell, opaque bytes): [shell.tunnel] + spt shell tunnel\\n— an optional reliable-ordered byte stream pair the taxonomy never\\ninterprets (first consumer: usbip URB). Not enveloped, not framed, not\\nspooled; the link lifecycle governs it (a link-break closes it). Reliable\\nordering means congestion surfaces as lag, never loss — so the tunnel is on-LAN only by design (not for use across a WAN). The byte relay is\\nproven same-node; cross-node operation (on-LAN only, by the same posture)\\nis not yet available — it lands when a cross-node consumer needs it.","breadcrumbs":"Shells » Four channels between owner and shell","id":"148","title":"Four channels between owner and shell"},"149":{"body":"Per-capability approval gates. Beyond the per- spawn gate, an individual [shell.capabilities.] may carry its own require_approval (with an\\noptional class_key scoping the grant finer than the verb — e.g. a remembered\\nHID-class attach never authorizes a storage-class attach). Spawn gates govern\\nwhether an instance may exist; capability gates govern whether a dangerous act may run. Ownership is owner-type-agnostic. Any non-shell endpoint may own, spawn,\\ndrive, command, link, and tunnel a shell — a Gateway as readily as an agent.\\nControl-exclusivity keys on the owner’s endpoint id, never its type: a\\ndifferent endpoint (even of the same type) cannot drive your shell. Lifecycle extras: persistent shells auto-online with their owner; wake_command runs a watcher while offline (exit code 86 = wake); a shell\\nwith can_shutdown = true may suspend its own owner ( api owner-shutdown)\\n— fail-closed otherwise.","breadcrumbs":"Shells » Two safety properties","id":"149","title":"Two safety properties"},"15":{"body":"The header is the only mandatory section: [adapter]\\nname = \\"mock\\"\\nkind = \\"harness\\" # or \\"shell\\" (a driven surface)\\nversion = \\"1.0.0\\"\\nmin_spt_core_version = \\"1.0.0\\" # compat gate, readable before any install/update\\nhostable_types = [\\"LiveAgent\\", \\"ReadyAgent\\", \\"Worker\\"] Inbound: your harness’s hook events, each firing one spt api command: [hooks.SessionStart]\\nfires = \\"api seed --pid {parent_pid} --session-id {session_id} --adapter {adapter_name}\\"\\nreads = [\\"session_id\\", \\"parent_pid\\"]\\ncan_inject = true # this hook can surface text back into the agent\'s context [hooks.Idle]\\nfires = \\"api state idle\\"\\ncan_inject = false # no inject channel -> spt-core uses its sentinel/relay fallback can_inject is the load-bearing harness-varying fact: when a hook can’t put\\ntext in front of the agent, spt-core routes around it automatically. Outbound: opaque session templates spt-core spawns with {key} placeholders\\nfilled: [session.self]\\ncommand = \\"mock-session --id {id} --session-id {session_id}\\"\\ndetach = true\\nkeys = [\\"id\\", \\"session_id\\"] A real adapter’s template is your harness’s full command line — model, flags,\\ntools, everything — exactly as you’d type it. The rest declares history access ( [history]), env bridging ( [env.*]),\\ninput injection ( [inject]), and session identity ( [identity]). Every\\nsection beyond [adapter] is optional; the manifest reference covers them all.","breadcrumbs":"Quickstart: build an adapter » 2. Read the manifest","id":"15","title":"2. Read the manifest"},"150":{"body":"Getting started: a notification shell — install the\\nshipping spt-shell-notify adapter, drive a native toast from an agent, and\\ncopy its manifest for your own surface.","breadcrumbs":"Shells » Start here","id":"150","title":"Start here"},"151":{"body":"The fastest way to understand shells is the shipping one: spt-shell-notify renders\\nagent commands and surfaced notifications as native OS notifications\\n(Windows toast / Linux notify-send). Its manifest plus one small binary are\\nthe only glue to spt-core — no spt-core source, no SDK; the binary speaks\\nthe public spt api surface and nothing else. This page installs it, drives\\nit, and reads its manifest as the template for your own shell.","breadcrumbs":"Shells » Getting started: a notification shell » Getting started: a notification shell","id":"151","title":"Getting started: a notification shell"},"152":{"body":"$ git clone https://github.com/SaberMage/spt-shell-notify\\n$ cd spt-shell-notify\\n$ cargo install --path . # puts `notify-shell` on PATH\\n$ spt adapter add . # validates + registers the manifest\\nADAPTER_ADD:notify:Shell:Copy (registered)\\n$ spt shell spawn notify # mints an instance (notify-1) and launches it spawn mints a new instance identity — notify-1 — and launches the\\nbinary; it’s the creation act, not an on/off switch. The first spawn asks for\\napproval once (the manifest sets require_approval = \\"remembered\\"), and the\\ngrant persists.","breadcrumbs":"Shells » Getting started: a notification shell » 1. Install and spawn it","id":"152","title":"1. Install and spawn it"},"153":{"body":"Two render paths, by design: Explicit command — an owner agent drives a toast down the durable command\\nchannel: $ spt shell cmd notify-1 notify \\"build finished\\" \\"all 139 tests green\\" The resident binary drains its command frames ( spt api … poll --link) and\\nrenders. Commands are validated against the manifest’s declared vocabulary —\\na verb or arity outside [shell.capabilities] is refused before it ever\\nreaches the binary. Surfaced notification — no agent in the loop: $ spt subnet notify \\"deploy window opens in 10 minutes\\" A subnet-wide notification resolves to the node the user most recently\\ntouched, and spt-core spawns the shell’s [session.notif] template there —\\na native toast on the machine you’re actually at.","breadcrumbs":"Shells » Getting started: a notification shell » 2. Drive it from an agent","id":"153","title":"2. Drive it from an agent"},"154":{"body":"The complete contract for this shell, annotated: [adapter]\\nname = \\"notify\\"\\nkind = \\"shell\\"\\nversion = \\"1.0.0\\"\\nmin_spt_core_version = \\"1.0.0\\" [shell]\\n# Broker-launched; the {link_token} is the binary\'s only credential.\\nspawn = \\"notify-shell --link {link_token} --id {id}\\"\\n# A display is node-local; discovery never offers it off-node.\\nbroadcast = \\"same-node\\"\\n# Auto-online with the owner: the notification surface should be up\\n# whenever the user\'s endpoint is.\\npersistent = true\\n# First spawn asks once; the grant is remembered.\\nrequire_approval = \\"remembered\\"\\npre_close = \\"closing\\"\\nclose_timeout_ms = 2000\\n# Offline wake-watcher: reports wake (exit code 86) after a short settle.\\nwake_command = \\"notify-shell --wake\\" # The whole command vocabulary: one verb, two positional args.\\n[shell.capabilities.notify]\\nargs = [\\"title\\", \\"body\\"] # The notif render seam: spt-core fills the {notif_*} keys and spawns this\\n# detached when a notification surfaces at an endpoint this shell serves.\\n[session.notif]\\ncommand = \'notify-shell --render-title \\"{notif_from}\\" --render-body \\"{notif_body}\\"\'\\ndetach = true\\nkeys = [\\"notif_id\\", \\"notif_from\\", \\"notif_subnet\\", \\"notif_body\\"] What the binary itself does (three modes, ~one file): resident ( --link …): calls api bind-shell --link to come\\nonline, then loops api poll --link draining command frames and\\nrendering them. one-shot render ( --render-title/--render-body): the [session.notif]\\ntemplate — render and exit. wake watcher ( --wake): run while the instance is offline; exiting\\nwith code 86 signals “wake me”.","breadcrumbs":"Shells » Getting started: a notification shell » 3. Read the manifest","id":"154","title":"3. Read the manifest"},"155":{"body":"A shell is worth building whenever agents should drive something —\\na desktop widget, a robot, a lamp, a game character, a sensor feed: Start from this manifest; change name, spawn, and the [shell.capabilities] vocabulary to your verbs. Your binary needs exactly three behaviors: bind with the link token, drain commands ( api poll --link, or declare command_receipt = \\"http\\"/ \\"stdin\\" if those fit better), and optionally emit sensory\\npayloads back ( api emit … --type --link — declared in [shell.sensory], delivered only to a live owner session: sensors\\nreport the present, never the past). Need more than discrete commands? A link can also carry a drive channel\\n( [shell.drive] — continuous, latest-wins real-time input like a stick or\\nscroll, never spooled) and an opaque tunnel ( [shell.tunnel] — a\\nreliable-ordered byte stream the taxonomy never interprets, on-LAN only).\\nSee the four channels\\nfor when to reach for each, and gate a dangerous verb with a per-capability require_approval (+ optional class_key). Any endpoint type may own a\\nshell — a Gateway as readily as an agent. Pick lifecycle behavior: persistent for always-up surfaces, ephemeral = true for fire-and-forget ones, wake_command if the\\nsurface can wake its owner. spt adapter add . and spt shell spawn . Field-by-field details: the manifest reference;\\nthe shell-side api calls: the spt api reference.","breadcrumbs":"Shells » Getting started: a notification shell » 4. Make your own","id":"155","title":"4. Make your own"},"156":{"body":"spt-core keeps itself current without ever interrupting your agents, and\\nwithout trusting anything unsigned.","breadcrumbs":"Self-update » Self-update","id":"156","title":"Self-update"},"157":{"body":"No endpoint process terminates or suspends during a self-update. The\\ndaemon’s broker (holding PTYs, child processes, sockets) stays up; the brain\\n(all logic) swaps under it. A hosted session’s process id and byte stream are\\nidentical before and after.","breadcrumbs":"Self-update » The invariant","id":"157","title":"The invariant"},"158":{"body":"Every release ships SignedRelease metadata: an Ed25519 signature over the\\nrelease’s artifact digests. Every binary embeds the two-key trusted set — an active primary and a\\nnever-used offline recovery key. Verification requires a valid signature\\nfrom a trusted key and a matching artifact digest; an unverified binary\\nnever reaches the apply step. Losing the primary key is a non-event: the next release is signed with the\\nrecovery key (already trusted by every deployed binary) and rotates in a\\nfresh primary. Adapters sign their own content. A file_pull adapter update is\\nverified against the adapter author’s key from its manifest; a delegated\\nupdate is trusted only when the manifest attests the delegated updater\\nverifies its own content ( self_verifies). spt-core’s release keys never\\nvouch for adapter bytes.","breadcrumbs":"Self-update » The trust chain","id":"158","title":"The trust chain"},"159":{"body":"Peer-propagated: one node fetches a release; paired nodes offer/fetch staged\\nreleases from each other, each verifying independently before staging.\\nUpdating is consent-gated by default — a notification surfaces at your\\nmost-recently-active endpoint, and spt update apply is the explicit ack\\n(it re-verifies the staged release before touching the live daemon).\\nFull-auto is an explicit opt-in. To bootstrap a node with no peer to pull from — or to update on demand — spt update fetch pulls the latest signed release from the origin and stages\\nit, then spt update apply installs it. spt update fetch --apply does both\\nin one step (and still installs when the latest was already staged, so it is\\nthe reliable one-shot “get me to the latest”). ( --apply since v0.18.0) After self-updating, spt-core ripple-updates registered adapters through\\neach manifest’s declared [update] avenue.","breadcrumbs":"Self-update » How updates move","id":"159","title":"How updates move"},"16":{"body":"Two layers of validation, both mechanical: Schema — your manifest must validate against manifest.schema.json.\\nThe schema is generated from the same code that parses manifests, so it is\\nalways current; closed vocabularies (adapter kinds, history strategies,\\nupdate avenues, …) are enums in it. Registration — spt adapter add parses, validates (including\\ncross-field rules the schema can’t express), and registers in one step: $ spt adapter add ./mock-adapter\\nADAPTER_ADD:mock:Harness:Copy (registered)\\nADAPTER_INSTALL_SKIP: no [update] avenue (manifest-only adapter)\\n$ spt adapter list\\nmock: Harness Copy active (from ./mock-adapter) A bad manifest is rejected here with a message naming the offending field —\\nnothing half-registers.","breadcrumbs":"Quickstart: build an adapter » 3. Validate and register","id":"16","title":"3. Validate and register"},"160":{"body":"An adapter can run a second, adapter-owned step after its primary update\\navenue resolves, under the same spt adapter update. Declaring an optional [update.post] sub-table ( command required; an attestation-only self_verifies flag) lets one lever both pull the adapter’s .spt (e.g. from gh_release) and run an in-harness sync (e.g. a plugin updater). The\\npost-step: runs unconditionally — even when the primary avenue was a no-op (its own\\nidempotent check decides what changes); receives a published JSON line on stdin describing the just-resolved\\nupdate ( adapter_applied, version, previous_version, adapter_dir, …;\\nadditive keys only — ignore unknown); decides the post-update notice via stdout — custom text supersedes the\\nstatic [update].message, the reserved sentinel !!update-message!! fires\\nthe static message, empty prints nothing; is failure-isolated — if it fails, spt-core warns loudly and falls back\\nto the today behavior (an applied update fires [update].message); a\\ncommitted pull is never rolled back. The exact stdin keys, sentinel, and notice precedence are in the manifest [update.post] reference.","breadcrumbs":"Self-update » Composite adapter updates — a delegated post-step (since v0.16.0)","id":"160","title":"Composite adapter updates — a delegated post-step (since v0.16.0)"},"161":{"body":"spt update · the consent notification flow ( spt notif) — CLI reference.","breadcrumbs":"Self-update » Commands","id":"161","title":"Commands"},"162":{"body":"Generated from the spt binary’s own --help output ( cargo run -p xtask -- gen) and drift-gated in CI — this page cannot disagree with the binary. Do not edit by hand.","breadcrumbs":"CLI reference » CLI reference","id":"162","title":"CLI reference"},"163":{"body":"spt — a harness-independent core for an agent ecosystem: inter-agent messaging, live-agent\\nlifecycle, terminal hosting, P2P networking, seamless self-update. Docs:\\nhttps://sabermage.github.io/spt-releases Usage: spt [OPTIONS] [COMMAND] User commands: adapter Adapter registration: what this node can drive/launch daemon The per-machine daemon: run, stop, or read node status grant Consent grant store: gated capabilities held on this node help Print this message or the help of the given subcommand(s) notif Inspect and acknowledge notifications rc Attach a local terminal to a broker-held endpoint PTY subnet Subnet membership: status, create, show-code update Self-update operations Agent commands: api Harness-contract inbound surface (hook entry points) endpoint Endpoint operations: list, lifecycle, fork, digest, access how-to Task-oriented instructions for agents: how-to ready Become reachable: register the perch and listen (blocks) ring Send and block for a reply (body read from stdin) send Send a message (body read from stdin); fire-and-forget shell Shell instances: mint, list, drive, tear down owned surfaces whoami Print this session\'s own perch id Options: --json Emit machine-readable JSON instead of the human view. Honored by the read/status commands (list, whoami, status, description, role, the *-list queries, how-to); action commands ignore it -h, --help Print help -V, --version Print version","breadcrumbs":"CLI reference » spt","id":"163","title":"spt"},"164":{"body":"Adapter registration: what this node can drive/launch. The node-local registered set (one command for harness and shell adapters). Feeds creation-time\\nadapter selection, shell discovery, and the self-update ripple. Usage: spt adapter [OPTIONS] Commands: add Register an adapter from a local path (a dir holding manifest.toml, or the manifest file itself) or from GitHub (--github user/repo, cloned under adapters/_github/). Manifest-first: an invalid manifest registers nothing. Install is the first update — the declared [update] avenue is conducted once after recording remove Soft-deregister: hidden from new-creation/discovery; existing and live instances keep running. The manifest\'s optional uninstall template is conducted only with --force until quiesce detection lands list List registered adapters (active and soft-deregistered), each followed by its shipped + local profiles as composite options version Print a registered adapter\'s declared version — the [adapter].version from its manifest. Resolves the option\'s merged view like the other adapter commands; exit 1 if the adapter is not registered create-profile Create (or overwrite) a local profile — a node-local sparse overlay registered beside the adapter that survives adapter add re-registration. The overlay TOML is read from --from or piped stdin (empty = a placeholder profile to populate later with set-string). Refuses a name shadowing a shipped profile, an invalid name, or an overlay that loosens a consent floor — nothing is written unless every check passes delete-profile Delete a local profile. Refuses a shipped profile name (adapter-owned, immutable) and errors if no local file exists get-string Read a [strings] dot-path from an adapter option\'s merged view ([:profile] ). Resolves through the profile overlay like every other consumer; prints the value (strings raw, else JSON). Exit 1 if the key is unset. Strings are data — never executed digest-proof Prove an adapter\'s [digest] extractor against a real log sample. Runs the declared extractor over --sample (or the declared source) and prints the parsed contract records, the rendered digest, and every dropped line with its reason — the author-time answer to \\"spt endpoint digest returns nothing\\" (no silent empty). Exit 1 if any line drops or nothing parses translate-proof Prove an adapter\'s [message-idle-translation-binary] against an inbound event. Spawns and feeds the declared translation binary exactly as the daemon does at idle-delivery — sends the init line then the --event envelope and reads back the emitted keystroke-command stream ({key}/{text}/{delay_ms}/{commit}), printed author-readable. This is the EMIT half ONLY: it proves the binary\'s spawn-feed-emit contract; it does NOT exercise the daemon\'s atomic PTY apply or controller buffering. Fills {id} and {session_id} into the envelope the same way the daemon does (use --session to pin the session id). Exit 1 if the binary fails to spawn, emits nothing, emits no commit, or emits an unparseable line set-string Set a [strings] dot-path on a local profile (:). Sugar over editing the overlay file; refuses a shipped profile and a bare option (a local target is required — create-profile first) update Update registered adapters that ship from their own GitHub releases: compare each [update] avenue = \\"gh_release\\" adapter\'s latest release version against the installed one and, when newer, fetch the release archive, verify it against the declared signing key if any (else trusting HTTPS + GitHub), and re-register. With no name, sweeps every gh_release adapter; with a name, updates just that one use Set or clear the active-profile pointer — the default [:profile] a harness session binds to when no --adapter is given. spt adapter use [:profile] points every host binary the adapter declares at it (run once per host binary you support); --clear drops the pointer (resolution falls back to the freshest-registered adapter). Never changed by install or update help Print this message or the help of the given subcommand(s) Options: --json Emit machine-readable JSON instead of the human view. Honored by the read/status commands (list, whoami, status, description, role, the *-list queries, how-to); action commands ignore it -h, --help Print help (see a summary with \'-h\')","breadcrumbs":"CLI reference » spt adapter","id":"164","title":"spt adapter"},"165":{"body":"Register an adapter from a local path (a dir holding manifest.toml, or the manifest file itself)\\nor from GitHub (--github user/repo, cloned under adapters/_github/). Manifest-first: an invalid\\nmanifest registers nothing. Install is the first update — the declared [update] avenue is\\nconducted once after recording Usage: spt adapter add [OPTIONS] [PATH] Arguments: [PATH] Local manifest source (omit when using --github or --release) Options: --github GitHub source user/repo — shallow-clone the repo and register the clone root. Manifest-first, then install via the declared [update] avenue --json Emit machine-readable JSON instead of the human view. Honored by the read/status commands (list, whoami, status, description, role, the *-list queries, how-to); action commands ignore it --release GitHub release source user/repo — fetch the adapter archive asset from the release and register it: ships built binaries, source-free and versioned (the pattern for a monorepo whose adapter is a subdir) --tag Release tag for --release (default: the latest release) --asset Release asset name for --release (default: adapter.spt — a tar archive whose root holds manifest.toml + strings/ + binaries) --gh Force the gh CLI transport for --release (the private-repo path; gh honors OAuth + GH_TOKEN, so spt custodies no token). Mutually exclusive with --https. Default: auto (gh when installed+authed, else HTTPS) --https Force direct HTTPS transport for --release (public repos). Mutually exclusive with --gh. Default: auto -h, --help Print help","breadcrumbs":"CLI reference » spt adapter add","id":"165","title":"spt adapter add"},"166":{"body":"Soft-deregister: hidden from new-creation/discovery; existing and live instances keep running. The\\nmanifest\'s optional uninstall template is conducted only with --force until quiesce detection\\nlands Usage: spt adapter remove [OPTIONS] Arguments: Options: --force Conduct the manifest uninstall template now, without waiting for quiesce --json Emit machine-readable JSON instead of the human view. Honored by the read/status commands (list, whoami, status, description, role, the *-list queries, how-to); action commands ignore it -h, --help Print help","breadcrumbs":"CLI reference » spt adapter remove","id":"166","title":"spt adapter remove"},"167":{"body":"List registered adapters (active and soft-deregistered), each followed by its shipped + local\\nprofiles as composite options Usage: spt adapter list [OPTIONS] Options: --json Emit machine-readable JSON instead of the human view. Honored by the read/status commands (list, whoami, status, description, role, the *-list queries, how-to); action commands ignore it -h, --help Print help","breadcrumbs":"CLI reference » spt adapter list","id":"167","title":"spt adapter list"},"168":{"body":"Print a registered adapter\'s declared version — the [adapter].version from its manifest. Resolves\\nthe option\'s merged view like the other adapter commands; exit 1 if the adapter is not registered Usage: spt adapter version [OPTIONS]