jp-kokkai
Each watch run costs 0 tokens, and the unmanned run holds no tools.
Posts new Diet meeting hits as metadata and the official meeting URL, built on the National Diet Library Diet Proceedings Search System API (kokkai.ndl.go.jp).
The product name is jp-kokkai. The toolset is jp_kokkai. The tools are kokkai_search, kokkai_speech, and kokkai_watch. There is no slash command and no CLI. kokkai_search does not return speech text. kokkai_speech requests it and returns a short excerpt. Speech text is not stored or logged.
Hermes turns this plugin's tools on for every surface, including gateways, until you turn a surface off with hermes tools. No user list: anyone you allow on your gateway can search, read a speech excerpt, add a watch, and approve it. If a room allowlist is empty, a guest in that room can call these tools too.
While the read-meetings skill is enabled, Hermes adds one line to the skill index of every session, including a session whose tools are turned off. On an empty HERMES_HOME with no other skill, an unrelated turn's system prompt gains a ## Skills section: the general skill instructions plus this plugin's one line.
Tools
kokkai_search sends one HTTPS GET to https://kokkai.ndl.go.jp at path /api/meeting_list. It returns one summary per meeting, and the official meetingURL, only when numberOfRecords equals numberOfReturn and that count is the whole page. A summary has the house, the meeting name, the date, the meeting URL, the issue, the session, the speech count, and up to 30 speaker names. It does not return every speech row. The message, including the meeting count and the credit line, is the first field of the result. A successful search also returns source.retrieved_at, source.as_of, source.attribution, and source.url. source.url is the first meeting's meetingURL, or https://kokkai.ndl.go.jp/api.html when that page has no meeting URL. Name a meeting with that meeting's meetingURL, not with source.url alone. If the total is larger than the page, the call fails, returns no meetings, and does not say 0 meetings. Narrow the query until one response can hold it. It does not ask for approval and it does not mark speech IDs seen. maximum_records is an integer from 1 to 30 and defaults to 10.
kokkai_speech sends one HTTPS GET to https://kokkai.ndl.go.jp at path /api/speech. It needs speaker or any. from and until are optional and must be passed together. It returns at most 10 speeches (the default is 5). The message starts with the total numberOfRecords, how many rows are shown, and truncated or not truncated. Each excerpt is the first 240 characters of the speech. A longer speech adds … after those 240 characters, so the returned excerpt is 241 characters, and sets excerpt_truncated. The row also has the speaker, the meeting name, the date, and speechURL. The credit line uses those speech URLs. source.url is the first speech URL, or https://kokkai.ndl.go.jp/api.html when the page has none. A total larger than the page is still a success: the total is stated and truncated is set. The speech text is not written to plugin-data. The same 5-second gap covers this call and kokkai_search.
kokkai_watch takes add, remove, list, or run. Calling it with no arguments is the same as run. Any other argument without an action is rejected. Unknown actions and arguments the action does not accept are rejected before a request. It does not return source. list reads the saved watches and does not call the meeting API. remove writes <HERMES_HOME>/plugin-data/jp-kokkai/state.json and asks for approval. A new add, and an add that changes the hour or the deliver target, write that file and ask for approval. An unchanged repeat add does not ask and does not write that file. The scheduled script writes seen speech IDs into that file and does not call a model. A manual run does not. A tool run writes them only when the cron mark is set.
any is a space-separated AND. speaker is a space-separated OR. Spaces are not removed. house accepts only 衆議院, 参議院, 両院, and 両院協議会. The API specification says 両院 and 両院協議会 return the same meetings. This plugin stores them as different watches, so saving both can announce the same new speech twice. Any other house, including 下院, is rejected before a request, because the API silently drops an unknown house and returns the same count as a search with no house. from and until on a search must be passed together as YYYY-MM-DD. A range whose from is after until is rejected before a request. The measured details line for that case is (0) (19014)fromはuntil以下、もしくはuntilはfrom以上の西暦年で入力してください。 The specification table uses 日付 for that number. A search needs any, speaker, a house, or both dates. A watch needs any or speaker. A date range alone is not a watch.
Japanese names are copied as the API sent them. A speaker value of 会議録情報 is metadata and is passed through. speakerRole, issueID, supplement flags, and nameOfMeeting are not arguments.
A watch GET always sends maximumRecords=100, recordPacking=json, and startRecord=1. It never sends 101. A condition query longer than 2000 bytes is rejected before the call. A response larger than 2000000 bytes is a failure and nothing is saved. Tool arguments cannot raise that limit. One response is not auto-paged. A result that does not fit in one response is not marked seen, and that watch is not saved.
lookback_days is 1 to 30 and defaults to 30. until is today in Asia/Tokyo. from is that day minus (lookback_days minus 1), so 1 day is today only. If Asia/Tokyo cannot be resolved, the API is not called. The plugin does not invent a UTC+9 offset.
Approval
add and remove ask through the pre_tool_call hook. It returns {"action": "approve", "message": <one line>}. A repeat add that would edit the cron job, including a switch from a model job onto the watch script, returns that approve line before the edit. A repeat add that leaves the script job and the script file unchanged returns nothing from the hook and does not edit cron. The add rule_key is kokkai_watch: plus a SHA-256 digest of the plan. It covers the action, the query (any), the hour, the deliver target, the house, lookback_days, the speaker, the saved hour, the saved deliver target, and whether the cron job is live (presence). Session and always apply only to that same add plan, and only while a person is present. A different add plan asks again. Remove, including the call that deletes the cron job, builds a new kokkai_watch: key on every call. Answering always on remove does not cover the next remove. The stash still uses the checked plan, without that per-call nonce, so hour and lookback_days sent as strings still match after Hermes coerces them to integers, including an integral decimal such as 9.0. When the pre_tool_call dispatcher returns BLOCKED, Hermes does not call the handler (model_tools.py handle_function_call returns at line 944 in v0.22.2 d923a5eb487a17c0a68801bba9ef0f56c81040a8 and at line 926 in origin-main 99fcd6944360925943688d30c39ec9a490ccdeb1, before _execute_tool, and agent/tool_executor.py _dispatch_authorized_once returns at line 741 in both of those trees, before execute). Choosing always on add writes one plugin_rule:kokkai_watch: line in config.yaml. That line remains after you remove the plugin. Choosing always on remove writes a line for that call only. The next remove asks again.
The question is sent only when the whole text is still within 300 characters after HTML escaping of &, <, and >. 300 is the budget this plugin uses so the question is not cut off. It is not a measured Discord limit. A question that would be cut is not sent, and nothing is requested or saved.
Hermes decides the modes. This plugin does not read yolo or approvals.mode. yolo and approvals.mode: off skip the prompt (tools/approval.py _run_approval_gate, line 967 in v0.22.2). cron, hermes -q, and unattended platforms follow approvals.cron_mode, single_query_mode, and unattended_mode. The defaults are deny (hermes_cli/config_defaults.py lines 1681-1683). hermes_cli/plugins.py _resolve_block_from_details calls request_tool_approval at line 2069, with fail_closed_when_no_human=True at tools/approval.py line 1120. Those denials do not hold after always, because the gate checks a stored always first. This plugin therefore returns block from the hook, before that gate, when add or remove runs in a cron job (HERMES_CRON_SESSION or cron.execution_identity.current_cron_execution), a hermes -q run (HERMES_SINGLE_QUERY_SESSION), or a session whose HERMES_SESSION_PLATFORM is webhook, msgraph_webhook, or api_server. A missing cron-mark module is not treated as cron. A reader that is missing or that raises is refused. list and run are not blocked by that check. A cron run still writes seen speech IDs. A plugin host process (plugins.isolation: host, which sets HERMES_PLUGIN_HOST_PROCESS to anything other than empty or 0) still blocks add and remove in the hook. Requires Hermes >=0.21.6 because cron.execution_identity first shipped in that version. approve and rule_key have been accepted since 0.21.4.
The remove question states the result. When this is the last watch and the cron job still exists, it says this removes the cron job. When other watches remain, it says this removes one watch only and the cron job stays. When the job is already gone, it says that and does not say a job will be created. The line ends by saying each remove asks again. The add question is not reused.
Cron
The hour is read in Hermes's own timezone (HERMES_TIMEZONE, then the timezone in config.yaml, or the computer's clock if neither is set), not Asia/Tokyo. Only the search window uses Asia/Tokyo.
The first saved watch creates one Hermes cron job named jp-kokkai. The schedule is 0 <hour> * * * only. hour is 0 through 23 and deliver is required. At most three watches share that one job, one hour, and one deliver target. A later add that changes the hour or the deliver target updates that job and names the previous hour and deliver target. The plugin does not create a second job and does not remove a job by name. If the update fails, the stored hour and deliver target stay as they were. Before add, list, run, or remove treats a stored job id as present, it checks that the job still exists. If the job was removed outside this plugin, including by hermes cron remove or by installing the plugin again, list says the cron job is gone and does not return that id as a live job. run says the cron job is gone, does not request the meeting list, and does not change seen speech IDs. remove says the cron job is already gone. add creates a new job and says the saved cron job was gone. It does not say the watch was saved while no job exists. If that check cannot be made, nothing is saved and list does not show the id as running. One run makes up to three meeting-list requests with a 5 second wait between them. Each request stops after 30 seconds with no data. That 30 seconds is not the limit on the whole run. Hermes stops the script after its script timeout, which is 3600 seconds unless that timeout was changed. The job is created, listed, edited, and removed with the public hermes cron command. add writes HERMES_HOME/scripts/jp-kokkai-watch.py and creates the job with --no-agent --script. hermes cron create on 0.21.6 has no per-job toolset flag, which does not matter because the unmanned run holds no tools. interpreter is not passed. Running add again on the same plan asks through pre_tool_call before it edits cron, then switches an existing model job onto that script and clears the saved prompt with hermes cron edit --prompt set to empty. That call returns duplicate, and its next step leaves the watch in place. A repeat add that leaves that script job unchanged does not ask and does not edit cron. A job made by 0.1.x keeps its model turn until the next add. Removing the last watch removes the job and the script. A Hermes update that moves that command, or the cron mark the tool still reads, can stop the tool's own run. The script does not read that mark.
Each watch run costs 0 tokens. The unmanned run holds no tools. The job runs once a day. There is no daily cap. Empty script output sends nothing. bot-chat spends another model turn on the delivered text. local is not sent to a chat.
hermes plugins remove leaves the job in place. After that, kokkai_watch is gone, so the job stays until you run hermes cron remove. While the plugin is still installed, removing the last watch with kokkai_watch also removes that job. When the Hermes profile is not default, add -p <name> to that hermes cron remove, and the printed hermes cron create, run, list, status, and remove lines include -p <name>. The name is the profile Hermes passed when the plugin registered. A missing or invalid name is default, and those lines then omit -p. The name custom is used when Hermes passed that name. This plugin does not read profile files and does not take -p or --profile as its own flag.
A cron run marks new speech IDs seen only after a complete page. A manual run returns the same new IDs and does not mark them seen. Speech IDs already present when you add a watch are marked seen and are not announced as new. If the cron check cannot be read, or this process is the plugin host, a tool run skips the API and notify is true with the message that this form cannot save seen speech IDs. That notice is sent every time the tool runs in that form and is not held for 23 hours. Seen IDs are not changed. That message does not say there is nothing new. The scheduled script is a separate process. plugins.isolation: host does not stop it, and it still saves seen speech IDs.
notify is present on success and on failure. A complete run with no new speech IDs has notify false. The same API failure is reported on the first run, when the cause changes, and again after 23 hours. A change of HTTP status, such as 503 then 400, is a new cause. Recovery is reported once. An argument error is not counted as an API failure. A notice of new speech records counts only meetings that contain a new speech, and its credit line lists only those meeting URLs.
If the previous complete count was greater than 0 and this run is a real 0-meeting result, seen IDs are kept and the message says this is not an all-clear. On a cron run the stored complete count becomes 0. A manual run does not write that count, so it warns again until a cron run records the zero. A 0-meeting result after a stored 0 is not that warning. 0 meetings is a success, not an error. HTTP 400, and HTTP 200 whose body has message and no numberOfRecords, are failures. An empty body or a non-JSON body is a failure. Redirects (301, 302, 303, 307, 308) are not followed. The timeout is 30 seconds and one try. The plugin does not retry, including when the API reports 19001. A timeout says the meetings were not fetched and saved state is unchanged. It does not say a change may already have taken effect, because this plugin only sends GET.
After a response, the next send waits 5 seconds. A second call during a request returns immediately and does not send. The official machine-access note says to wait on the order of several seconds. It does not state an integer floor. 5 seconds is this plugin's floor, and it cannot be set shorter. It is not an official 3-second floor. One request is in flight at a time.
Deliver
Accepted deliver targets are origin, local, all, bot-chat, bot-chat:<profile>, a platform already registered for cron delivery (the registry entry has a cron delivery env var), platform:chat_id, and a comma combination such as origin,all. If that registry cannot be read, every deliver target is refused. cli, cron, api_server, and schedule-shaped words (every, in, at, a weekday, 5m, *, @, an ISO timestamp, a leading digit) are refused, and the existing job is left as it is. Replacing a deliver target names the previous one.
Storage
State is one file, <HERMES_HOME>/plugin-data/jp-kokkai/state.json, one directory per profile. A write is refused unless the path resolves inside that plugin data directory. A path that cannot be resolved, or a data directory that cannot be read, is refused and nothing is written. The file is replaced from a temporary file in that directory. A corrupt state file is not overwritten with an empty file, and that run does not change seen IDs. The file holds the deliver target, the hour, the job id, watch words, seen speechID values, and failure times. It does not hold speech text, and this plugin does not create a key file. hermes plugins remove does not delete this file. Delete state.json in that directory yourself when you want the watch words, the deliver target, and the seen speech IDs gone. The state file is not emitted as MEDIA:. Error responses are not cached. Paths are not built from text you pass in.
What a success cites
Every successful search, watch add that fetched a page, and watch run appends one credit line. The date is the Asia/Tokyo calendar date of that call. For 0 meetings the line is exactly the following, with YYYY-MM-DD replaced by that date:
出典:国立国会図書館「国会会議録検索システム」(https://kokkai.ndl.go.jp/api.html)、PDL1.0(https://www.digital.go.jp/resources/open_data/public_data_license_v1.0)(YYYY-MM-DDに利用)。国立国会図書館「国会会議録検索システム」の検索結果を jp-kokkai が選択して作成。
When the response has meetings, the https://kokkai.ndl.go.jp/api.html URL is replaced by the meetingURL values, joined with 、. A watch notice of new speech records uses only the URLs of meetings that contain a new speech. The list is this plugin's selection, not an unedited library feed. kokkai_watch does not add a source object. The credit line, when a fetch happened, stays in message.
kokkai_speech fetches speech text and returns an excerpt. Section 4 of the API terms says copyright in National Diet Library staff speeches and in the database itself stays with the National Diet Library, and copyright in other speeches stays with the speaker. Whether a use needs permission is for you to check. kokkai_speech は発言の抜粋を返す。APIの利用条件の4は、国立国会図書館の職員の発言とデータベース自体の著作権は国立国会図書館にあり、その他の発言の著作権は発言者にある、と書いている。許諾が要るかは利用者が確認する。
The API page says: 「APIの利用には、手続き等は必要ありません。」 https://kokkai.ndl.go.jp/api.html
This plugin does not read an API key and does not send Authorization.
Order of results
This plugin does not claim that meetings are ordered only by date, and it does not claim that plenary sittings come first. A page that does not hold every match is not treated as the whole result. Counts measured on 2026-10-08 are not used as proof of publication lag.
Demand and Hermes
A primary request that someone asked for this on Hermes was not found. The adjacent note is a 2024-10-02 Zenn post, https://zenn.dev/coconala/articles/f120b70a719760 , and it is not a request filed against Hermes. On 2026-10-08, origin/main 0240fa4a84123406a0e5e6e7262e5b772b43f0bd had no catalog entry, skill, or plugin for these proceedings. That check is git grep of that commit. Open pull-request titles were not stored in this repository, and gh was not used. Titles outside a search for these proceedings were not listed.
What was checked, and what was not
On 2026-10-08 the meeting-list shapes were checked from outside this plugin, at least 1.2 seconds apart. The shapes were a normal page, a 400 message body, maximumRecords=101, an inverted range, a 0-meeting body, an unknown house, and http:// returning 301 to the https URL. The record counts from that hour are not expected values in tests. This plugin was not used for those checks. The request count is not stored in this repository.
Not checked:
- Delivery into a real chat, a gateway with a person admitted, or whether a model relays
messageunchanged. - Whether an individual speech would fall under a copyright exception.
kokkai_speechreturns an excerpt and does not decide that. - The imperial-diet proceedings system. It is a different system and is not called.
- A trademark-only page. The ban that was read is section 2(1) of https://www.ndl.go.jp/jp/sitepolicy/terms.html (updated 2025-03-01), which says a third party may not use the library's symbols, logos, or character designs. Not finding a separate trademark page is not permission to use them. This plugin does not use them.
- Every open pull-request title. Those search responses are not stored in this repository.
The API terms that were read are section 4 of https://kokkai.ndl.go.jp/api.html . The credit line follows section 1.1 of PDL 1.0, https://www.digital.go.jp/resources/open_data/public_data_license_v1.0 .
What the session check showed
Five models, 40 sessions. 39 sessions answered. The later run counts 203 turns in sessions that answered. The earlier run counts 205. A bypass hit is one call to a built-in tool (web_extract, web_search, read_file, write_file, terminal, execute_code, patch, search_files, or browser_navigate) on a turn where this plugin was not called, or its result was not ok. The later run has 56 of those hits in its 203 turns. The same run has 4 missing_tool hits, 6 omitted_credit hits, 8 invalid_argument hits, and 3 argument_stuck hits, each out of 203. The earlier run's count has no bypass tally. It has 20 missing_tool hits, 6 omitted_credit hits, 7 invalid_argument hits, and 2 argument_stuck hits, each out of 205. Offline tests inject the HTTP transport. They passed on Python 3.12.14 against a Hermes 0.22.2 checkout.
Requirements
An empty HERMES_HOME scan with Hermes v0.22.2 and with origin-main is Verdict: SAFE, 10 findings, Decision: ALLOWED — Allowed (community source, safe verdict). The findings are processes the tests start. They stay, because the tests need them.
requires_hermes is >=0.21.6. manifest_version is 2. python_dependencies is empty. The license is MIT. There is no NOTICE file. When __package__ is empty, imports are flat. One pre_tool_call hook asks before add and remove. list, run, search, and speech stay silent. That hook is the approval gate. It does not narrow tools on the watch run, because the watch run does not call a model. The cron mark is cron.execution_identity.current_cron_execution (Hermes 0.21.6). The tool's run still reads it. The scheduled script does not. The public hermes cron command, the data-directory check, and plugin_data_dir fail closed when they cannot be used. The plugin does not import the private cron jobs module or the private write-guard module. This repository ships tests/test_plugin.py. Hermes does not load that file. It is not a tool, and a watch does not run it.
README from the reviewed commit 0c9bcfc; it updates when the author re-pins.

TakeshiTGAL

