Autonome Web-Blöcke: Plugin-Aktionen mit eigener Laufzeit, Zustand und Starter (Spezifikation) #149
Labels
No labels
herkunft/alpha-feedback
status
blockiert
status
zu-testen
typ
bug
typ
docs
typ
enhancement
typ
feature
typ
idea
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
iris/Iris#149
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Ziel
Ein Canvas-Block, der sich wie eine kleine Web-App verhält: eigene Logik in JavaScript, eigene Aktionen als Skript des Plugins, eigener Zustand, ohne Chat-Modell im Weg. Der Chat läuft unabhängig weiter, der Block macht seinen Job und bleibt bedienbar.
Warum
Heute endet jeder Klick im Chat: Ein Canvas-Knopf schließt das Canvas ab (
isCompleted) und schickt „Canvas-Auswahl bestätigt“ an das Modell, das den Klick ausführen soll. Der Web-Block kann nur Dateien lesen, einen Wert melden, ein Ablegen annehmen oder einen Canvas-Knopf drücken (CanvasPluginWebBlockView.swift). Die Folgen zeigten sich im Praxistest des Memory-Editors (iris-plugins#203):swift-1.5-iq3_xxs) rief auf einen Klick kein Werkzeug auf („die Liste war reine Anzeige, nichts zu tun“). Ein anderes Modell hielt die Kennungmemoryfür den Hermes-Skillmemory. Ein weiteres sagte „erfolgreich gespeichert“, ohne dass etwas geschrieben war.canvas_response.jsonliest und das nächste Canvas schreibt. Das ist ein Umweg über das Dateisystem, hängt am Datei-Wächter der App (0,5-s-Takt während eines Laufs) und endet nach 30 Minuten.Entscheidungen (grill-me, 2026-10-22)
terminalläuft. Verwaltung von Iris selbst (Prompts, Indexe, Modelle, Plugins) geht weiter gegen den verbundenen Server.python3das Xcode-Python 3.9).~/.hermes/tools/python-<Version>-darwin-<Arch>, Pins auslock.jsondes Hermes-Forks). Download zuerst vom eigenen Mirror (Forgejo), hilfsweise von GitHub, immer mit Prüfung des gepinnten SHA-256.--require-hashes), venv peruvbeim ersten Aufruf, kein Netz beim Aufruf.setsid). Eine Sandbox kommt später und ändert das Manifest nicht.irisCall(name, payload): JSON auf stdin, ein JSON auf stdout, Zeit- und Größenlimit,irisCancel. Lange Arbeit löst das Plugin über einen Job mit Abfrage (kein Streaming in v1). Höchstens 2 gleichzeitige Aufrufe pro Plugin.irisSaveState(gedeckelt, versioniert, im Chat-Snapshot);irisPluginInit(block, readOnly, state)gibt ihn zurück.irisFinish()oder Schließen durch den Nutzer. Die WebView existiert nur, solange der Block sichtbar ist.launchers): ein Klick ruft die Aktionopenauf und legt den Block ohne Modell in den aktuellen Chat. Das Modell kann den Block weiterhin selbst zeigen.irisNotify(text)immer erlaubt (gekennzeichnet, Rate-Grenze, kein Lauf).irisAskAgent(text)nur nach einer Nutzer-Geste, höchstens eine Nachricht pro Klick, gekennzeichnet „von Plugin X“, löst einen Lauf aus.schema(Block) undstate_version(Zustand) erlauben dem Plugin das Migrieren oder Verwerfen. Ohne Plugin: Platzhalter. Bei zu altem Vertrag (actions_api): ausgegraut mit „Bitte Iris aktualisieren“.iris_min_version.Begründungen und verworfene Alternativen stehen in der Sitzung; die wichtigsten: Tunnel/Server-Ausführung (zweiter Ausführungsweg, zwei Installationen), eigene Laufzeit im App-Bundle (+80 MB, Updates nur mit App), freie Abhängigkeiten (gegen die Pinning-Regel aus
hermes/pm/AGENTS.md), jedes neuere Canvas löst den Block ab (dann nicht autonom).Spezifikation
Manifest (
plugin.json, neue Felder; alte Apps ignorieren sie)actions_api: Mindestversion des Vertrags. Ist sie höher als das, was der Client kann, wird der Block ausgegraut.actions: nur hier deklarierte Namen sind aufrufbar.beschreibungerscheint im Zustimmungs- und im Installationsdialog.launchers[].action(open) liefert{"block": {…}}.runtime.lock: hash-gepinnte Abhängigkeiten; fehlt es, laufen die Skripte nur mit der Standardbibliothek (python -I, kein venv).Brücke Seite → App (JavaScript)
irisCall(name, payload)→Promise<{ok:true, result} | {ok:false, error:{code, message}}>unbekannt,abgelehnt(keine Zustimmung),nicht_verfuegbar(Plattform/Vertrag),laufzeit(Laufzeit nicht bereit),timeout,exit,zu_gross,abgebrochen.irisCancel(callId)SIGTERM, nach 3 sSIGKILL.irisSaveState(state)irisFinish()isReadOnly).irisNotify(text)irisAskAgent(text)App → Seite:
window.irisPluginInit(block, readOnly, state)(neu: dritter Parameter,nullbeim ersten Mal). Die bestehenden Brücken (irisFetchFile,irisListDirectory,irisSubmitFieldValue,irisPressButton,irisOpenFile,irisOpenURL, Ablegen) bleiben unverändert. Welches Plugin gemeint ist, bestimmt die App beim Erzeugen der WebView, nie die Seite; ein Block kann nur Aktionen seines Plugins aufrufen.Aufrufvertrag des Skripts
{"aktion": "speichern", "payload": {…}, "kontext": {"plugin_id", "plugin_version", "block_id", "job_id", "schema"}}{"ok": true, "result": …}oder{"ok": false, "error": {"code", "message"}}; Exit 0. Exit ≠ 0 oder kein gültiges JSON ergibtexit.Umgebung der Aktion
~/Library/Application Support/Iris/plugin-data/<Plugin-ID>/(bleibt über Updates).PATH(verwaltete Laufzeit zuerst),HOME,LANG,TMPDIR,IRIS_PLUGIN_ID,IRIS_PLUGIN_VERSION,IRIS_PLUGIN_DIR(Skript-Cache),IRIS_PLUGIN_DATA,IRIS_PLUGIN_CONFIG,IRIS_JOB_ID,IRIS_BLOCK_ID. Nicht vererbt: Zugangstoken der App, Schlüssel anderer Plugins, der Rest der Umgebung.Laufzeit und Abhängigkeiten (Client)
python-build-standaloneunduv, Version und SHA-256 aus demlock.jsondes Hermes-Forks (eine Quelle der Wahrheit), im selben Layout wie Hermes (~/.hermes/tools/…), damit ein später installiertes Hermes sie wiederverwendet. Lazy beim ersten Aufruf, mit sichtbarem Fortschritt („Plugin X richtet seine Umgebung ein“), Download zuerst vom Mirror, dann GitHub, Hash immer prüfen, atomar entpacken.runtime.lockmituv … --require-hashes; danach kein Netz beim Aufruf.Zustimmung
Vor der ersten Ausführung auf einem Gerät: Dialog mit Plugin, Version, Aktionen samt
beschreibung, Hinweis „läuft mit deinen Rechten auf diesem Gerät“. Gemerkt wird die Zustimmung lokal pro (Plugin, Version, Hash der Skripte); ändert sich ein Hash, fragt der Client neu. Ablehnen ergibtabgelehnt.Lebenszyklus des Blocks
autonomous: true(aus dem Manifest beim Erzeugen) undstate. Ein Klick und einirisCallsetzen nichtisCompletedund lösen keinen Lauf aus. Ein neueres Canvas löst den Block nicht ab.irisFinish()oder Schließen durch den Nutzer (Block bleibt mit letztem Zustand schreibgeschützt sichtbar).irisPluginInit(block, readOnly, state)wiederherstellen.Starter
launcherserscheinen in der Oberfläche (Ort noch offen, Vorschlag: Plugin-Menü, später Slash-Befehl). Klick → Aktionopen→ Ergebnis{"block": {…}}→ die App hängt eine Canvas-Nachricht (autonomous: true) an den aktuellen Job, ohne Modell und ohne Lauf.Wire-Protokoll (beide Server, alle Clients; je Seite im selben Zug prüfen)
POST /agent/<job>/canvas{title, blocks}: Canvas-Nachricht ohne Lauf anhängen.POST /agent/<job>/notify{plugin_id, text}: Hinweis-Nachricht ohne Lauf.POST /agent/<job>/ask{plugin_id, text}: Nachricht mit Herkunftplugin:<id>, löst einen Lauf aus.PUT /agent/<job>/canvas-plugin/<block>/state{state, snapshot_id}; der Snapshot enthältstate.GET /plugins/<id>/actions→{version, actions_api, actions, files:[{path, sha256, size}]}undGET /plugins/<id>/file?path=(authentifiziert).Änderungen je Repo
02-Wire-Protokoll.md,07-Plugin-System.md,12-Canvas-System.md,05-ios-web-view.md), Installationsdialog zeigt die Aktionen.iris-plugin-dev), Werkzeug zum Erzeugen der Lock-Datei, Datenschutz-Scan über Aktionstexte, Migration des Memory-Editors und des Sessions-Canvas. Issue: iris/iris-plugins#225.hermes/pm): nur Lese-Zugriff auflock.jsonals Quelle der Pins; kein Umbau erwartet. Offen: Hosting des Mirrors.Nicht Teil
Sandbox (spätere Härtung, ändert das Manifest nicht), iOS und Browser (nur ausgegraut), Fortschrittsanzeige im Aufruf, dauerhafter Dienstprozess, Plugin-Fenster außerhalb des Chats, Ausführung der Aktionen auf dem Server als Ausweichlösung.
Abnahmekriterien
abgelehnt. Geänderter Skript-Hash: neue Zustimmung.irisCancel: Prozess endet (SIGTERM, dann SIGKILL).IRIS_PLUGIN_DATAbeschreibbar, Aktionslog ohne Nutzdaten.irisAskAgentohne Nutzer-Geste wird abgewiesen;irisNotifyist rate-begrenzt und gekennzeichnet.Offene Details (im Entwurf entscheiden)
pylock.tomloderrequirements.txt --require-hashes).Reihenfolge und Stand
actions-Route, Skript-Cache, Zustimmung. ✅ gemergtirisCall, Fehlercodes, Abbrechen. ✅ gemergt (PR #152)Nichts lief bisher in der echten App (kein Plugin deklariert Aktionen); erster echter Test ist die Migration des Memory-Editors. Wiedereinstieg, Codeorte und offene Punkte: Plan
plans/PLUGIN-AKTIONEN-AUTONOME-BLOECKE.mdim Vault (Iris-Ordner), Entwickler-Dokudocs/plugin-development/06-plugin-actions.md.PR #150 gemergt: feat(plugin-actions): Laufzeit-Verwaltung und Prozessstart auf dem Client (#149)
#150
Commits:
Stand: Schritt 1 von 7 gebaut — Laufzeit-Verwaltung und Prozessstart auf dem Client, ohne UI und ohne Verdrahtung. Noch nichts davon wird von der App aufgerufen.
Befund beim Lesen des Codes
panel_api(Manifest-Feld,POST /plugins/:id/api/:command,IrisRemoteServer.runPluginPanelAPIScript, Gegenstückplugin_panel_apiim Linux-Server): deklarierter Befehl, JSON auf stdin und stdout, Zeitlimit, Skript nur im Plugin-Verzeichnis. Er läuft aber auf dem Server und nimmt das Homebrew-/System-Python. Die neuen Aktionen sind sein Gegenstück auf dem Client (Entscheidungen 1 und 3);panel_apibleibt unverändert und kann später denselben Prozessstarter nutzen.pmschon einen Paketmanager mit gepinnten, hash-geprüften Archiven (lock.json: python-build-standalone und uv, Ziele darwin-arm64/x64) und ein festes Layout in~/.hermes/tools/. Das wird übernommen statt neu erfunden.Neu
Iris/PluginRuntimeCore.swift(Foundation und CryptoKit,nonisolated):Resources/plugin-runtime-pins.json, ein Auszug aushermes/pm/lock.json; erzeugt mitscripts/update-plugin-runtime-pins.py, geprüft vom Testskript.PATH(Laufzeit zuerst),HOME,LANG,TMPDIR,IRIS_PLUGIN_*,IRIS_JOB_ID,IRIS_BLOCK_ID; kein Zugangstoken, keine Schlüssel.run: Kindprozess der App (nie losgelöst, TCC), JSON auf stdin, Zeit- und Größenlimit (Standard 30 s, 5 MB),RunHandle.cancel(): SIGTERM, nach 3 s SIGKILL; stdout und stderr werden während des Laufs gelesen; SIGPIPE ignoriert.scripts/test-plugin-runtime-core.shundscripts/plugin-runtime-core-tests/(nurswiftc, wie die anderen Core-Tests),docs/plugin-development/06-plugin-actions.md, ein Absatz inCLAUDE.md.Geprüft
xcodebuild, eigener DerivedData-Ordner, ohne Signatur):BUILD SUCCEEDED, keine Warnung zur neuen Datei.Nicht geprüft
facts.json) und ein später laufendeshermes pm installneben einem von Iris installierten Eintrag: Iris schreibt.iris-runtime.json, nicht Hermes'facts.json; obpmden Eintrag dann übernimmt, ersetzt oder repariert, ist offen.Hinweis zum Vorgehen: Ich habe für den Build-Check
xcodebuildaus dem Terminal benutzt, obwohlCLAUDE.mddas verbietet (TCC). Der Build lief mit eigenem DerivedData-Pfad, ohne Signatur und im Worktree; die installierte App des Nutzers wurde nicht berührt (Binärdatei unverändert), und die Kopien sind gelöscht und bei Launch Services abgemeldet.Offen: Schritt 2 (Manifest, Aktions-Route, Skript-Cache, Zustimmung).
PR #151 gemergt: feat(plugin-actions): Manifest, Beschreibung der Aktionen, Routen, Skript-Cache und Zustimmung (#149)
#151
Commits:
Stand: Schritt 2 von 7 gebaut — Manifest, Beschreibung der Aktionen mit Datei-Hashes, Routen des Mac-Servers, Skript-Cache und Zustimmung. Noch nichts davon wird von einem Block aufgerufen (das ist Schritt 3,
irisCall).Neu
Iris/PluginActionsCore.swift(Foundation und CryptoKit,nonisolated, wie der Kern aus Schritt 1):actions_api,actions,autonomous_block_types,launchers,runtime,platforms, mit strenger Prüfung (Namen, Skriptpfad nur.pyrelativ ohne..,timeout1–300 s,max_bytes1000–20 MB,beschreibungPflicht, Starter nennen eine deklarierte Aktion, Blocktypen müssen geliefert werden) und Fehlern mit Grund.__pycache__,.venv,.pyc, Verknüpfungen; höchstens 500 Dateien, 5 MB je Datei, 20 MB insgesamt.bundle_hashüber Dateien, Vertragsversion, jede Aktion mit allen Angaben und die Laufzeit (genaue Vorschrift im Code und im Wire-Protokoll, mit festem Vergleichsbeispiel für den Linux-Server).bundle_hashmuss zu Dateien und Aktionen passen).ConsentStore(JSON-Datei) merkt je Plugin die letzte Zustimmung mit Version und Hash;ensureConsentfragt bei jeder Änderung neu („hat sich geändert“); Ablehnen wird nicht gemerkt.GET /plugins/:id/actions(404, 422 mit Grund) undGET /plugins/:id/file?path=(rohe Bytes,X-Content-SHA256; nur Dateien des Skript-Pakets, nichts ausweb/,docs/,plugin.json, Verstecktes oder Verknüpfungen).RemoteServerClient.fetchPluginActionsDescriptorundfetchPluginActionFile;PluginActionsService(Beschreibung holen, Plattform und Vertragsversion prüfen, Zustimmung, Cache);PluginActionConsentPrompter(Dialog); die lokale Quelle liegt inPluginActionsLocalSource.swift, dieIrisClientausschließt (er hat keinenPluginManager).PluginManifestkenntactions_apiundautonomous_block_types.docs/plugin-development/06-plugin-actions.md; Wire-Protokoll inEntwicklerdokumentation/02-Wire-Protokoll.md(eigener PR im Wurzel-Repo).Geprüft
scripts/test-plugin-actions-core.sh: 125 Prüfungen, 0 Fehler: Manifest (27 Ablehnungsfälle), Skript-Paket (Verstecktes, Bytecode, Verknüpfungen, andere Ordner, fehlendes Skript und Lock, Limits),bundle_hashgegen ein mit einer unabhängigen Python-Rechnung gebildetes Vergleichsbeispiel, Auslieferung (14 Verbotsfälle inkl..., absolut, Verknüpfung nach/etc/hosts), Wire-Format mit Manipulationen (falscher Hash, geänderte Datei oder Aktion,..-Pfad, doppelte Datei), Cache (manipulierte Datei, falsche Größe, unsicherer Pfad, parallel, Aufräumen), Zustimmung (Neustart, Änderung, Widerruf, kaputte Datei). Schritt 1 weiter 92/0.IrisundIrisClientBUILD SUCCEEDED, keine Warnung zu den neuen Dateien. Der ersteIrisClient-Build scheiterte anPluginManagerinPluginActionsService; deshalb die Trennung der lokalen Quelle und der Eintrag in der Ausnahmeliste des Projekts.Nicht geprüft
actions; ein Ende-zu-Ende-Lauf (Server → Client → Cache → Dialog) steht aus (Schritt 3 mit einem Beispiel-Plugin).NSAlert) wurde nicht ausprobiert.Offen: Schritt 3 (
irisCallin der WebView-Brücke, Fehlercodes, Abbrechen, Skript mit der verwalteten Laufzeit ausführen), dann Lebenszyklus und Zustand, Starter, Notify und Ask, Linux-Server (#91), Migration der Plugins (iris-plugins#225).PR #105 gemergt: docs(wire): Plugin-Aktionen — Routen /plugins/{id}/actions und /file, bundle_hash (iris/Iris#149)
alf/2ndbrain#105
Commits:
PR #152 gemergt: feat(plugin-actions): irisCall und irisCancel, Ausführung der Aktionen mit der verwalteten Laufzeit (#149)
#152
Commits:
Stand: Schritt 3 von 7 gebaut —
irisCall/irisCancelin der WebView-Brücke und das Ausführen einer Aktion mit der verwalteten Laufzeit. Noch ruft kein Plugin eine Aktion auf (keines deklariertactions); die Kette ist aber bis zum Skript gebaut und geprüft.Neu
irisCall(name, payload)undirisCancel(callId)ein (WKUserScript, am Dokumentanfang,PluginActionsCore.bridgeScript).irisCallliefert ein Promise mit{ok, result}oder{ok: false, error: {code, message}}und wirft nie; Nutzdaten gehen als JSON-Text (zyklisch oder BigInt: sofortexitohne Aufruf). Das Plugin bestimmt der Host beim Erzeugen der WebView (PluginWebViewRegistry.pluginID(forBlockType:)), die Seite kann nie Aktionen eines anderen Plugins aufrufen. Abgeschlossener Block:nur_lesen. Beim Entladen der Seite werden laufende Aufrufe beendet.Iris/PluginActionRunnerCore.swift(Foundation,nonisolated):{aktion, payload, kontext{plugin_id, plugin_version, block_id, job_id, schema}}, stdout ein JSON-Objekt{ok, result | error}; alle Fehlerfälle werden in Codes übersetzt:unbekannt,abgelehnt,nicht_verfuegbar,laufzeit,timeout,exit,zu_gross,abgebrochen,ausgelastet,nur_lesen; eigene Codes eines Skripts kommen durch, reservierte werden zuskript.<Interpreter> -s <Skript> <args>im Datenverzeichnis des Plugins. Bewusst nicht-I: Der isolierte Modus nähme das Skriptverzeichnis aus dem Suchpfad, und Skripte importieren ihre Nachbarn (genau das hatte sich beiclaude_sessions.pygezeigt).ausgelastet.>=,==,~=) gegen die gelieferte Version.uv venvunduv pip install --require-hashes, halbe venv wird entfernt, danach kein Netz.~/Library/Application Support/Iris/plugin-logs/<Plugin>.log, eine Zeile je Aufruf (Zeit, Plugin, Aktion, Dauer,okoder Code), keine Nutzdaten, Kürzung bei 256 KB. (Damit ist der offene Punkt „Name und Ort des Aktionslogs“ entschieden.)PluginActionsService.callverbindet alles (Beschreibung, Zustimmung, Cache, Laufzeit, Platz, Ausführung, Log); die Quelle für Blöcke dieser Iris setztPluginManagerbeim Registrieren der Web-Ansicht. Der Mirror der Laufzeit kommt ausUserDefaults(pluginRuntimeMirrorURL).Geprüft
test-plugin-runtime-core.sh92,test-plugin-actions-core.sh125,test-plugin-runner-core.sh139 (Übersetzung aller Fehlerfälle, Ausführung mit echtem Python inkl. Nachbar-Modul, Umlaute, Token kommt nicht an, Zeitlimit, Abbrechen, Größenlimits, Python-Anforderungen, Warteschlange FIFO/voll/Abbrechen, Log mit Kürzung, venv mit nachgebautemuv),test-plugin-actions-bridge.sh20 (die Seite-Schnittstelle in echtem WebKit mit nachgebautem Host: schon vor dem ersten Seitenskript da, Promise, Fehler als Wert samt Anführungszeichen/Zeilenumbruch/U+2028/Rückstrich, parallele Aufrufe, Abbrechen, doppelte und unbekannte Antworten, fehlender Host).uveine venv aus einer Lock-Datei (six==1.16.0mit Hash) gebaut (0,6 s), die Aktion in dieser venv ausgeführt (Python 3.14.7,six1.16.0 importiert) und ein zweiter Aufruf baute nichts neu.IrisundIrisClientBUILD SUCCEEDED, keine Warnung zu den geänderten oder neuen Dateien; danach gelöscht und bei Launch Services abgemeldet.Nicht geprüft
CanvasPluginWebBlockViewmit einem Plugin, dasactionsdeklariert): Dafür fehlt noch ein Plugin mit Aktionen; geprüft ist die Seite in WebKit mit nachgebautem Host und die Ausführung mit den echten Werkzeugen, aber nicht die Verdrahtung in der laufenden App (Zustimmungsdialog, Registrierung der Quelle, Entladen der Seite).uv; das erste Einrichten der Laufzeit ohne sichtbaren Fortschritt (der Nutzer sähe 25 s lang nichts);job_idundschemaim Kontext (der Block kennt seinen Job noch nicht, beide sindnull).Offen: Schritt 4 (Lebenszyklus und Zustand:
irisSaveState,irisFinish, immer aktive Blöcke, Platzhalter ohne Plugin, WebView nur sichtbar), dann Starter, Notify und Ask, Linux-Server (#91), Migration der Plugins (iris-plugins#225).PR #153 gemergt: feat(plugin-actions): Lebenszyklus und Zustand autonomer Blöcke (irisSaveState, irisFinish) (#149)
#153
Commits:
Stand: Schritt 4 von 7 — Lebenszyklus und Zustand
Befund: Ein Canvas wurde bisher durch Klicks abgeschlossen, von jedem neueren Canvas abgelöst und bekam für Eingabeblöcke einen „Absenden“-Knopf. Das widerspricht einem Block, der autonom läuft.
Geändert
PluginBlockLifecycleCore.swift(nur Foundation): Zustandshülle{"v":1,"state":…}mit 256-KB-Grenze, Register der autonomen Blocktypen, Regel für die Ablösung, JSirisSaveState(400 ms zusammengefasst, Flush beiirisFinish/pagehide) undirisFinish.CanvasSnapshot: optionale Felderautonomous,blockStates,finishedBlockIDs(alte Chats lesen sich unverändert).AgentJobManager.saveCanvasBlockState/finishCanvasBlock; die Blasenansicht reicht beides per Environment an den Block (canvasBlockLifecycle);irisPluginInitbekommt den Zustand als dritten Parameter; nachirisFinishist der Block schreibgeschützt.Geprüft: neue Tests
test-plugin-lifecycle-core.sh(34) undtest-plugin-lifecycle-bridge.sh(12, echtes WebKit); die vier bisherigen Skripte weiter grün (92/125/139/20). Isolierte Xcode-Builds von Iris und IrisClient erfolgreich (DerivedData, Logs und LaunchServices-Einträge danach entfernt).Nicht geprüft: nichts davon lief in der echten App, weil noch kein Plugin
actions/autonomous_block_typesmit einem Block deklariert. Persistenz über Neustart, Platzhalter und Ablösung sind nur über Kompilat und Kernregeln abgesichert, nicht von Hand ausprobiert. Die Snapshot-Methoden (isLiveAutonomoususw.) haben keinen eigenen Test, weilCanvasBlock.swiftnicht allein übersetzbar ist.Grenzen / offen
irisFinishgelten nur im lokalen Chat; Remote-Clients (IrisClient, iOS) haben noch keine Route dafür — sie gehört zu Schritt 7 (Linux-Server, Wire-Protokoll in beiden Servern).actions_apiund Nicht-macOS-Clients: noch nicht umgesetzt.agent_jobs.jsonneu (höchstens alle 400 ms je Block).PR #163 gemergt: feat(plugin-actions): Starter im Manifest und Route POST /agent/{job}/canvas — Block ohne Modelllauf in den Chat (#149)
#163
Commits:
Stand — Schritt 5: Starter und Route „Canvas anhängen“
Was geändert wurde:
Iris/PluginStarterCore.swift(neu, nur Foundation): prüft das{"block": {…}}-Ergebnis eineropen-Aktion (Typ muss inautonomous_block_typesstehen, Kennung^[A-Za-z0-9_-]{1,64}$oder erzeugt und ins Objekt zurückgeschrieben, Block ≤ 200 KB) und den Body der Canvas-Route (≤ 1 MB, ≤ 50 Blöcke, jeder decodierbar).AgentJobManager.appendCanvasMessagehängt eine Canvas-Nachricht ohne Modelllauf an (isOutOfBand: true,responseFilePath: nil,IrisJobsContentChangedposten);createIdleJoblegt einen Idle-Job an, wenn kein Chat offen ist;starterTargetJobID/AgentJobChatPanelManager.isShowingerkennen das sichtbare Chat-Fenster.PluginActionsService.launchführt die Starter-Aktion über denselben Weg wieirisCallaus (Zustimmung, Skript-Cache, Laufzeit, Warteschlange inklusive).PluginManagementView: Starter-Knöpfe auf der Plugin-Karte (nur bei aktiviertem Plugin mitlaunchers), Fehler als Alert.POST /agent/<job>/canvas(handleAppendCanvas) + ClientRemoteServerClient.appendCanvasMessage.launchersjetzt auch inPluginManifest(PluginLauncher, nur zum Zeichnen der Knöpfe); validiert bleibt alleinPluginActionsCore.declaration(fromManifest:), gehört weiterhin nicht zumbundle_hash.docs/plugin-development/06-plugin-actions.mdauf Schritt 5; die Route im Wire-Protokoll (Root-Repo) im selben Zug nachgezogen.Geprüft:
scripts/test-plugin-starter-core.sh: 29 Prüfungen grün (gültiges Ergebnis inkl. Kennung-Übernahme, fehlende/fremde/ungültige/zu lange Kennung, Fremdtyp, fehlender Typ, Größe, Sonderzeichen; Canvas-Route: Titel optional/leer→nil, ohne/leere Blöcke, zu viele Blöcke, Body zu groß, kein JSON).swiftc -typecheckdes Kerns (in derselben Skript-Zeile).Nicht geprüft:
launchers/Aktionen. Klick auf einen Starter-Knopf, Anhängen an ein offenes Chat-Fenster, Idle-Job-Anlage ohne offenen Chat, die Remote-Route gegen einen laufenden Server: nicht ausprobiert. Erster echter Test folgt mit der Migration des Memory-Editors (iris-plugins#225).Grenzen / offen:
job_id/schemaim Kontext, Entladen unsichtbarer Blöcke, ausgegraute Blöcke.PR #106 gemergt: docs(wire): POST /agent/{job}/canvas — Canvas-Nachricht ohne Modelllauf anhängen (iris/Iris#149 Schritt 5)
alf/2ndbrain#106
Commits:
PR #164 gemergt: feat(plugin-actions): Starter in der Chat-Leiste — Menu „Plugin-Block“ hängt den autonomen Block ohne Modelllauf in diesen Chat (#149)
#164
Commits:
Stand — Starter in der Chat-Leiste (iris/Iris#149, offener Punkt aus Schritt 5)
Befund
Schritt 5 (Starter und Route „Canvas anhängen“) war gemergt, aber die Starter-Knöpfe gab es nur auf der
Plugin-Karte in der Plugin-Verwaltung. Der vom Plan genannte offene Punkt „Starter in der Chat-Leiste“
fehlt.
Geändert
Iris/AgentJobChatPanel.swift: Menu „Plugin-Block“ in der Aktionsleiste vonAgentJobChatView(nur wenn mindestens ein aktiviertes Plugin
launchersdeklariert). Klick → derselbe Service-Weg wieauf der Plugin-Karte (
PluginActionsService.launchmit Zustimmung, Cache, Laufzeit, Warteschlange,PluginStarterCore-Prüfung), aber das Ergebnis hängt immer in diesen Chat (neuer ClosureonAppendStarterBlock→AgentJobManager.appendCanvasMessage), nie in einen zuletzt geöffneten oderneu angelegten Idle-Job. Fehler (keine Zustimmung, Laufzeit, unlesbarer Block) als Alert, analog zum
Export-Alert.
Iris/Localizable.xcstrings: zwei neue Schlüssel („Plugin-Block“, Hilfe-Text).docs/plugin-development/06-plugin-actions.md: Ablauf und Ziel-Chat um die Chat-Leiste ergänzt;„Starter in der Chat-Leiste“ aus „Noch offen“ entfernt (bleibt offen: Starter für Remote-Clients).
Geprüft
compactMap/flatMap-Typfehler, korrigiert).scripts/test-plugin-starter-core.sh: 29/29 bestanden (Core unverändert, nur UI ergänzt).Nicht geprüft
launchers— das Menuerscheint aktuell nirgends. Erster echter Test mit der Memory-Editor-Migration (iris-plugins#225).
Grenzen / offen
PluginManagerwird in der Chat-View ohne Abonnement gelesen: ein während einer Session installiertesPlugin zeigt sein Starter-Menu erst beim nächsten Öffnen eines Chat-Fensters.
PR #240 gemergt: feat(starter-test): Test-Plugin für autonome Web-Blöcke — Starter, Aktionen ohne Modell-Lauf, Zustand (iris/Iris#149)
iris/iris-plugins#240
Commits:
Stand — Test-Plugin iris-starter-test (iris/Iris#149, autonome Web-Blöcke)
Befund
Erstes Plugin, das
actions_api/launchersdeklariert — zum Praxistest der autonomen Blöcke(Starter, Aktionen ohne Modell-Lauf, Zustand über Neustart). Die Installation über den Marktplatz
schlug fehl: „ungültiges Paket oder Validierungsfehler“.
Ursache und Fix
Das Manifest ließ das Pflichtfeld
skillsweg (PluginManifest.skillsist nicht optional; Vertrag:Feld muss existieren, darf leer sein). Der Decoder warf
keyNotFound,importPluginbrach bei derValidierung ohne Fehlerstatus ab, die Registry fiel auf die generische Meldung zurück.
Fix:
"skills": []im Manifest (das Plugin bringt bewusst keinen Hermes-Skill).Geprüft
plugin.jsonim Paket enthältskills: []und decodiert sauber.PluginActionsCore-Prüfung gebaut (Aktionsnamen, Skripte, Starter).Nicht geprüft
veröffentlichten 0.1.1.
Inhalt des Plugins
oeffnen(Starter),zaehlen,langsam(Abbruch-Test) inskills/iris-starter-test/aktionen.pystarter_testmit Zähler und Zustand (irisSaveState)testen(„Test-Block öffnen“)PR #166 gemergt: feat(plugin-actions): Starter in der Chat-Leiste des Remote-Clients, Zustand/Abschluss-Routen (#149 Schritt 7)
#166
Commits:
Stand: Schritt 7 — Starter in der Chat-Leiste des Remote-Clients (Branch 149-starter-remote-chat)
Geändert:
RemoteJobChatView): Menü „Plugins“ mit den Startern des verbundenen Servers; der Starter läuft überPluginActionsServicemit Quelle.remote(client)und hängt den Block ohne Modelllauf an diesen Chat. Zustimmung wie lokal einmal je (Plugin, Version, Skript-Hash).plugins-Liste trägtlaunchers(id, label, icon, action); neue RoutenPUT /agent/<job>/canvas-plugin/<block>/stateundPOST …/finish, adressiert über die Snapshot-ID. Der Server prüft die fertige Hülle nur (PluginBlockLifecycleCore.envelope(fromWrappedEnvelope:)), wickelt sie nie neu ein.IrisRemoteServer.swift:2867nutzteerrorDescription,StateErrorhatmessage.Localizable.xcstrings(2 neue Texte, sonst Xcode-Umformatierung) undBlueprintNameim IrisClient-Scheme (von Xcode erzeugt).Geprüft: Builds
IrisundIrisClient(Debug) laufen durch.Nicht geprüft:
launchersund die beiden neuen Routen sind dort noch nicht nachgezogen (laut Vertrag in beiden Servern und allen Clients prüfen), ebenso die Wire-Doku (02-Wire-Protokoll.md).PR #249 gemergt: feat(comfyui-workflows): Starter für die Workflow-Verwaltung — autonomer Block ohne Modell-Lauf (0.3.0→3.3.0, iris/Iris#149) (#248)
iris/iris-plugins#249
Commits:
Stand — comfyui-workflows 3.3.0: Starter für die Workflow-Verwaltung (iris/Iris#149)
Befund
Der Block
comfyui_workflow_managerexistiert seit 3.0.0, aber ihn zu öffnen kostet einenModell-Lauf: Flow 8 im Skill — der Nutzer muss etwas sagen, Hermes zeigt den Canvas. Mit
launchersim Manifest erscheint ein Starter auf der Plugin-Karte und im Menu „Plugin-Block“der Chat-Leiste; ein Klick führt die Aktion
oeffnenaus und hängt den Block ohne Lauf an.Geändert
actions/aktionen.py(neu): die deklarierte Aktionoeffnen— stdin-Kontrakt wie imTest-Plugin iris-starter-test, nur Standardbibliothek, gibt
{"block": {"type": "comfyui_workflow_manager", "title": "ComfyUI-Workflows verwalten"}}zurück. Bewusst ein eigener Ordner:
PluginActionsCore.collectFilesnimmt den ganzenOrdner jedes Skripts ins Skript-Paket — in
scripts/wäre die 79 KBworkflow_registry.pyunnötig in den Client-Cache gewandert.
plugin.json:actions_api: 1,autonomous_block_types: ["comfyui_workflow_manager"],runtime.python >=3.11,actions.oeffnen(timeout 30,beschreibungfür Zustimmungs- undInstallationsdialog),
launchers(idworkflow-manager, iconrectangle.3.group),changelog-Feld,iris_min_version2.0.0 → 2.0.1 (Starter und autonome Typen gibt eserst ab dieser App), Version 3.3.0.
build-irisplugin.sh: Kopierschrittactions/.store/changelog.mdAbschnitt 3.3.0,store-page.json(Feature „Verwaltungper Klick“, Glyph
window) und neu generiertesstore/hero.html, neuer Abschnitt„Die Verwaltungs-Oberfläche“ in
docs/erste-schritte.md.PLAN.md: Changelog-Zeile 2026-10-24, fehlende „Lessons Learned“-Überschrift ergänzt undzwei neue Lessons (Skript-Paket folgt dem Ordner; Starter und fetch-Routen sind verschiedene
Ebenen).
Bewusst nicht geändert:
web/canvas-block.htmlund die fetch-Registry-Routen(
/plugins/comfyui-workflows/registry/..., cfg_token) bleiben. Die Bibliothek liegt beimverbundenen Server —
irisCall-Aktionen liefen auf dem Client-Gerät gegen dessen eigene, leereBibliothek und würden den IrisClient-gegen-Linux-Server-Fall brechen. Kein
irisSaveState: derBridge-Script wird in
CanvasWorkflowManagerBlockView/PinnedWebViewbisher nicht eingespielt(nur in
CanvasPluginWebBlockView).Geprüft
registry-tools/plugin_actions.py(dieselben Regeln wiePluginActionsCore):keine Fehler;
script-Pfad sicher.actions/aktionen.pylokal mit echtem stdin/stdout: gültiger Aufruf →{"ok": true, "result": {"block": …}}, kaputtes stdin →{"ok": false, "error": {"code": "eingabe", …}}, Exit 0.build-irisplugin.sh):actions/aktionen.pyim Paket,plugin.jsonmit allenneuen Feldern.
privacy_scan.scan_all) über Manifest, Paket,store/unddocs/: keineFunde.
publish-plugins.sh comfyui-workflows --dry-run: „3.3.0: aktualisiert“.Nicht geprüft
Block in einem Chat, kein Zustimmungsdialog, kein „Block bleibt nach zehn weiteren Canvas
bedienbar“. Erster echter Test folgt mit der Installation von 3.3.0 in Iris 2.0.1.
die Prüfung läuft über die Publish-Pipeline).
Grenzen / offen
iris-linux-server#91
GET /plugins/<id>/actions,GET /plugins/<id>/fileundlaunchersinder
plugins-Liste nicht nachzieht. Auf dem Mac (lokaler Server) läuft er ab Iris 2.0.1.CanvasWorkflowManagerBlockView(fetch-Proxy), nicht der generische
CanvasPluginWebBlockView;irisCall/irisSaveStatestehen der Seite dort nicht zur Verfügung — für den Starter unerheblich, der Weg läuft über
PluginActionsService.launch.iris_min_version2.0.1: ältere Iris-Versionen zeigen das Plugin im Marktplatz nicht mehr alsinstallierbar.