@rsrsai/magpie 0.0.0-stage → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,51 @@
1
+ # respire-memory capabilities and boundaries
2
+
3
+ Applies to **v0.3.2–v0.3.3**. v0.3.3 handles observed Windows/macOS Bun errors during concurrent lock acquisition within the existing deadline, and updates npm distribution documentation. See the [review repair report](REVIEW-RESPONSE-0.3.1.md), [policy-correction report](TEST-REPORT-0.3.0.md) and [historical v0.2.1 acceptance report](TEST-REPORT.md). A working memory round trip is not proof of full engine-policy compliance.
4
+
5
+ ## Agent self-evolution through experience
6
+
7
+ Each newly observed user turn recalls memory, reads the complete Runtime policy, then reads selected records and relevant causal chains. The answering agent receives the full policy separately from untrusted historical memory. A host-execution receipt identifies the initial steps already performed; the agent must not fabricate unavailable tools or repeat host actions mechanically.
8
+
9
+ Every successfully archived writable turn must write and read back a process diary, even when there is no new long-term conclusion. New reusable facts, decisions and lessons are separately updated or attached to existing records. Explicit remember emphasizes the user's intent; it is not the sole storage trigger. Archival defaults to on and requires a configured generation model. Raw transcript capture remains separate and off by default.
10
+
11
+ This feedback loop changes reusable memory, not model weights. Readback confirms storage, not the truth of a conclusion. No general agent-quality or comparative performance benchmark is claimed.
12
+
13
+ ## Current feature matrix
14
+
15
+ | Capability | Implementation | Evidence / limitation |
16
+ | --- | --- | --- |
17
+ | Per-turn read | Recall, complete policy from summary.instructions, selected full records; causal chain for decision/lesson/agreement/error-like hits | Four protocol fixtures; real Runtime complete-policy injection inspected. Same-turn retries reuse durable results |
18
+ | Policy presence | Full policy is supplied every new turn, independently of the obsolete injectRules flag | Legacy small recall budgets do not clip policy; separate 128 KiB allowance plus fixed adapter; increases context cost |
19
+ | Explicit remember | Front end reads policy/candidates, reports pending and defers classification and writes | No direct foreground remember that bypasses classification; pending is not saved |
20
+ | Automatic archival | Default on; detached worker uses a separately configured Chat Completions model | Requires generation setup. Disabled/unconfigured archival is disclosed as incomplete, not successful |
21
+ | Mandatory process diary | Writable success requires a new trivial write in this archival and readback of its returned ID | No-write nothing-to-store is rejected. Existing long-term facts still must not be duplicated |
22
+ | Long-term organization | Restricted recall/show/chain/tree/taxonomy plus remember/update; shown parent required for new important records | Main-loop and fixture checks pass; destructive merges/configuration changes remain unavailable |
23
+ | Source evidence and long turns | Bounded evidence tools, segmentation and user-source reads before writing | 270,000-character automated case; no real-provider long-turn qualification |
24
+ | Persistence and failures | Encrypted queue, turn deduplication, progress, record IDs and unknown-write no-retry | Unknown writes stop archival; no exactly-once/power-loss guarantee |
25
+ | Read-only and caller control | Local/magpie-key only; explicit readOnly and engine restrictions remain authoritative | Shared-key fixture rejection checked; old host magpie-key classification remains limited |
26
+ | Optional raw capture | Trivial raw turn diary when explicitly enabled | Default off; distinct from model-produced process diary; no live raw-capture acceptance claimed |
27
+ | Settings and status | Settings page, Memory/Used/prior-save hints, adaptive local light/dark icon | v0.2.1 actual Magpie web rendering inspected; hints depend on the answering model |
28
+ | Delivery | Three-OS Node/Bun checks and tagged GitHub Release tarball/SHA256 | npm deferred; community registry delivery requires maintainer merge |
29
+
30
+ ## Strict requirements that remain unmet
31
+
32
+ **Magpie's completion notification arrives after the reply has completed.** This plugin therefore cannot guarantee the engine's strict requirement to store before the reply is sent. A verified process diary after completion does not satisfy that timing requirement. The implementation must not be described as complete engine-policy parity.
33
+
34
+ The gateway also lacks native client SessionStart/resume/compaction events, trustworthy automatic client plan-mode detection, guaranteed native status lines, and visibility into the client's filesystem/tool actions outside gateway traffic. Use readOnly explicitly for plan/no-write workflows.
35
+
36
+ ## Operational limits
37
+
38
+ - Known-dead PID locks recover; live contention waits up to five seconds. Successful writes remain completed when only session-notification metadata fails.
39
+ - Only active jobs count toward capacity. Terminal evidence is retained 24 hours; completed receipts and ordinary turn/session caches seven days. Compact unknown-write markers remain without old raw bodies.
40
+ - Worker jobs/control leases are bounded to five minutes, checked before each new generation/write. Disable/uninstall without another request is not an immediate cancellation signal; already accepted work can continue within the bound.
41
+ - Display hints default off. Complete policy/per-turn storage remain part of the explicit Respire integration, including empty recall results. Fresh identity checks are retained rather than caching account identity across requests.
42
+
43
+ - Only completed, visible, supported turns qualify. Lost notifications before enqueue, truncated replies, unresolved tools, sensitive content and unseen Responses previous_response_id history can prevent archival. These are incomplete rounds, not saved rounds.
44
+ - Raw images, audio and private reasoning are not archived. Missing history is not reconstructed.
45
+ - Project filtering is not authorization. Other client tools are outside this plugin's control.
46
+ - Archival sends turn/tool evidence and relevant memory to the configured provider and consumes model quota. Missing setup and disabled controls must be disclosed.
47
+ - State encryption keys live beside ciphertext; protect the whole directory. This is not an OS keychain.
48
+ - Changed controls reach workers on a subsequent request and cannot revoke an accepted write. Unknown writes must be checked against the actual library before any manual decision to retry.
49
+ - No exhaustive crash recovery, account-switch race, concurrent-load, power-loss, live all-protocol or native desktop rendering acceptance is claimed.
50
+
51
+ Installation and recovery commands: [README](../README.md).
@@ -0,0 +1,56 @@
1
+ # Magpie PR 553 review repair report
2
+
3
+ This responds to the review of community head 40fec72. It is an upstream engineering report, not a claim that community CI has approved this package. The community package links upstream reports rather than shipping copies as its own evidence.
4
+
5
+ The first repair candidate, v0.3.1, failed Windows CI with a transient EPERM while removing an empty lock directory under concurrent access. Its tag is retained and no release assets were published. The follow-up candidate is v0.3.2, with bounded Windows cleanup retries. It also corrects a separate empty-recall bug: hiding display hints must not discard the complete engine policy. The existing checks now cover empty recall with hints off across all four adapters.
6
+
7
+ The first v0.3.2 main-branch CI run passed macOS/Linux but exposed a separate fixture error on Windows: the final pending-write probe reused a 20-second abort signal created before 512 fsynced capacity records. RPC already has its own per-request deadline. The fixture now uses that deadline for independent calls and a fresh signal for its segmented model operation; production request deadlines are unchanged.
8
+
9
+ ## Review disposition
10
+
11
+ | Point | Changes / disposition |
12
+ | --- | --- |
13
+ | 1: tests not discovered by CI | A respire-memory.test.mjs Bun adapter runs the existing behavioral checks. Checks bootstrap a fresh temporary HOME, reject a non-temporary test HOME, use os.tmpdir() and remove their state. The Bun path no longer requires an external node command. Upstream CI also runs the adapter |
14
+ | 2: completed jobs exhaust capacity; indefinite raw evidence | The 512 cap counts queued/running jobs only. Terminal raw evidence expires after 24 hours; completed receipts and ordinary turn/session caches after seven days. Compact unknown-write markers remain without old raw bodies. Cleanup is lazy on foreground maintenance/worker startup; it cannot run while the host is closed. Library memories are not deleted by local-state cleanup |
15
+ | 3: stale/live locks and false failure after successful writes | Shared PID ownership locks recover only proven-dead owners; live contention waits up to five seconds. Four real child processes compete in the checks. Unknown owners in older empty locks still require operator review. Session-notification failure is recorded separately and does not turn verified writes into an unconfirmed write |
16
+ | 4: worker per request | A host performs at most one startup recovery scan per state directory, spawning only if active work exists. Newly enqueued work wakes a worker; duplicate jobs do not. No empty worker is spawned for unconfigured archival |
17
+ | 5: hot-path writes / unbounded turn state | Controls are persisted only when the configuration signature changes or the heartbeat is due. Foreground requests no longer create a session record merely to set rulesSent. Turn/session state expires. **Account-identity TTL caching was deliberately not adopted:** without a Runtime account-revision binding, it creates a switch-account window in which old-library cache contents can be reused. Fresh account checks remain a safety boundary |
18
+ | 6: unstable history after restart/options | Earlier turns, including a current tool continuation, restore their exact block from durable records after reset. Keys include only recall-affecting options plus conversation identity. Existing older whole-options-hash records cannot safely be guessed into the new scheme; an upgrade may cause a one-time cache-prefix change |
19
+ | 7: generated host names and stale UAs | New stored provenance is neutral, and Runtime/generation UAs are neutral and version-independent. Generated policy adapters no longer name the host. Actual user/history content and the complete engine policy are not falsified or scrubbed of factual project names |
20
+ | 8: provenance repository metadata | Community repository.url points to magpie-community/plugins, with directory=packages/respire-memory. Homepage/bugs remain upstream |
21
+ | 9: default output changes | Memory/Used hints default off. **Per-turn policy reads and automatic diary archival remain enabled as the explicit product contract requested by the user.** Empty recall does not disable those requirements. Archival needs generation configuration; without it, incomplete setup is disclosed and raw turns are not queued. Turning policy off by default would contradict the requested behavior |
22
+ | 10: detached work after disable | Jobs/control leases are bounded to five minutes from acceptance, configuration signatures must match, and account/mode/control are checked before new generation/write calls. Expired work does not call the model. Disable/uninstall without another hook invocation is not an immediate notification; accepted work can continue within the bound, and an accepted write cannot be revoked |
23
+ | 11: community README / upstream reports | Community README is rewritten around its actual package, CI entry, monorepo dist/provenance and limitations. Personal paths and upstream release-workflow instructions are removed. Upstream reports are linked and explicitly labeled, not packaged as community CI evidence |
24
+ | 12: placement / name | Entry/README row moved after the existing memory-hook entries. The requested product/display name respire-memory is retained rather than renamed to Respire |
25
+
26
+ ## Final automated verification
27
+
28
+ - [Three-OS upstream CI at 1a080fb](https://github.com/risense-ai/respire-magpie/actions/runs/41192351640): Windows, Linux and macOS passed with Node 24 and Bun 1.3.6, including the discovered adapter and package dry-run.
29
+ - [Supplemental community compatibility run](https://github.com/risense-ai/respire-magpie/actions/runs/41192352925) checked out community contribution **df58d566b9ddca3aaced1b490035e0a26468a554** on Linux: **4193 pass, 3 skip, 0 fail**, 4196 tests across 328 files; root checks and the 22-file package dry-run passed. The package's adapter was discovered and passed within the full suite.
30
+ - These are upstream-hosted runs. Community PR CI still requires maintainer approval; this report does not replace that approval or claim the PR has merged. The failed earlier Windows runs remain visible and documented above.
31
+
32
+ ### Local verification details
33
+
34
+ Windows; Node 26.13.0; host Bun 1.4.4. The existing checks pass under Node and via the Bun-discovered test adapter. A separate empty-PATH run of the adapter passed after removing its external-node dependency.
35
+
36
+ The checks now include real cross-process lock contention, known-dead legacy/new lock recovery, live contention, notification failure after verified storage, 512 completed jobs versus active capacity, evidence/receipt expiry without resetting the retention clock, retained unknown-write markers, stale/changed controls, stopping before the next generation after disable, exact durable history restoration and neutral User-Agent headers. Four-protocol, policy ordering, diary/readback, encryption and unknown-write no-retry checks remain.
37
+
38
+ An initial **full community suite** run on Windows reported **4100 pass, 28 skip, 56 fail and 2 errors** across 328 files. One failure was this package's now-fixed external-node/PATH dependency; the adapter subsequently passed without PATH. Other failures were reported in other packages and were not individually diagnosed or modified here. The final full community suite is **not claimed green**; community CI still needs its actual execution/approval. A successful upstream package CI is separate evidence.
39
+
40
+ ## Live acceptance and privacy
41
+
42
+ A native local gateway request using the existing real model returned the required short reply. Its detached archival stored and read back the process diary and completed with **5 restricted calls**. This validates the revised locks/control lease in the ordinary-turn path; it is not a live reboot, days-long retention or exhaustive concurrency qualification.
43
+
44
+ Temporary enabling applies to the entire local gateway. Another live session also passed through it during the probe. The original disabled state/options were restored, the private test control lease was revoked, and the unrelated raw copy in this project's verification state was removed. That session's engine memories were not modified. No unrelated session content or IDs are published in this report.
45
+
46
+ ## Follow-up found during npm publication
47
+
48
+ The v0.3.3 preflight at 4fc5411 exposed another Windows acquisition race: an owner-file open returned EPERM when empty-directory recovery removed the just-created directory before ownership was published. An earlier main-branch run at 96fd8da also failed at that path, although its separate release verification passed. The passing v0.3.2 runs above therefore did not establish absence of this intermittent failure.
49
+
50
+ The stronger check also exposed Windows scandir EPERM during a recovery probe while a competing release removed the directory. v0.3.3 retries those acquisition-only open/probe failures within the existing deadline; it never enters a critical section without verifying sole ownership. The existing eight-writer check now runs eight batches and verifies all 64 increments. Permanent failures still terminate; unknown/live owners and uncertain writes are not deleted or replayed. npm distribution first published v0.3.2 using Chrome-confirmed authentication; the next patch is used to validate the browser-configured OIDC connection.
51
+
52
+ At 7f170e2, Windows passed, while macOS Bun 1.3.6 reported EINVAL on owner-file open in the four-process contention probe. The same bounded owner-publication retry now includes that observed macOS Bun error. No failed check was skipped or replaced by a same-code rerun.
53
+
54
+ ### Remaining host limits
55
+
56
+ The engine's strict before-send persistence requirement still needs an appropriate host callback or a client-owned loop: the current notification is after reply completion. Native desktop rendering, live all-protocol sessions, exhaustive crash/power-loss/account-switch recovery, npm first publication and marketplace approval remain outside the proven result. See the [feature matrix](FEATURES.md) and [0.3.0 correction report](TEST-REPORT-0.3.0.md).
@@ -0,0 +1,69 @@
1
+ # respire-memory 0.3.0 policy-correction report
2
+
3
+ Date: 2026-10-31. This supersedes **policy-compliance claims**, not the historical observations in the [0.2.1 report](TEST-REPORT.md). The earlier successful memory round trip did not prove mandatory per-turn read/store behavior.
4
+
5
+ ## Result and remaining gap
6
+
7
+ Local automated checks passed. Real local Runtime policy injection, ordinary-turn diary archival, explicit-intent fact plus diary archival, independent readback and fresh-session recall passed with the corrected code. Original failed draft probes were retained and not replayed.
8
+
9
+ **Strict engine-policy parity remains incomplete:** Magpie calls onResponse after completion, so this plugin cannot guarantee storage before the reply is sent. Missing completion notifications, missing history, unsupported client events and unavailable write permission remain explicit limitations. Successful background archival is not proof of the before-send requirement.
10
+
11
+ ## Corrections
12
+
13
+ | Former gap | 0.3.0 behavior |
14
+ | --- | --- |
15
+ | Simplified rules only once per session | Each new observed user turn recalls, reads complete Runtime policy, then reads selected records; legacy injectRules=false cannot suppress policy |
16
+ | Simulation put policy in the wrong envelope field | Reads the actual summary.instructions contract; fixtures now match the live Runtime |
17
+ | Small recall budgets cannot hold full policy | Complete policy has its own bounded allowance, up to 128 KiB plus fixed host adapter; no policy clipping |
18
+ | Policy mixed with untrusted history | Full policy is supplied in a separate policy block; historical memories retain the untrusted-reference boundary |
19
+ | Agent may repeat unavailable CLI tools | Host receipt identifies completed reads and prohibits fabricated tools; corrected live replies did not emit fake tool calls |
20
+ | Front-end explicit remember bypassed classification | Prefix is intent only; foreground recalls/reads policy and reports pending, worker classifies and writes |
21
+ | Optional/no-op successful archival | Archive defaults to on; successful writable archival requires this archival's trivial diary write and returned-ID readback, even without a new long-term fact |
22
+ | Model must initialize mandatory read gates itself | Host performs recall, complete policy and mode reads before the first model generation, and supplies real tool receipts |
23
+ | No causal-chain tool | Chain is available to the restricted worker and used for relevant foreground records; device metadata is retained |
24
+ | Same project argument rejected as an override | An explicit identical project is normalized; a different project remains rejected |
25
+ | Poor failure diagnostics | Safe tool command/round/error progress is retained; an unknown write aborts immediately and is not replayed |
26
+
27
+ Disabled/unconfigured archival and read-only mode are disclosed as incomplete/no-write workflows. Raw conversation capture remains off by default and separate from the mandatory model-produced process diary.
28
+
29
+ ## Existing automated checks
30
+
31
+ Run npm run check, bun scripts/check.mjs and npm pack --dry-run from the source directory. No new test file was added; the existing checks were extended.
32
+
33
+ New assertions cover:
34
+
35
+ - Per-turn recall → complete policy ordering, including explicit remember intent.
36
+ - Policy still present with a 1024-byte recall budget and injectRules=false.
37
+ - Missing policy produces an incomplete-workflow diagnostic rather than a success claim.
38
+ - Actual policy-envelope shape and default archive=true in runtime and manifest.
39
+ - Worker host startup ordering before model work.
40
+ - No-write nothing-to-store is rejected; successful segmented and detached-worker cases include a verified process diary.
41
+ - Identical explicit project accepted; different project rejected.
42
+
43
+ Existing four-protocol, URL/caller/selected-secret guards, encryption, unknown-write no-retry, read-only, source validation and real detached child-process checks remain in place. These are isolated fixtures, not live all-provider/all-client acceptance. Tagged three-OS CI results are linked from the release/PR; the old 0.2.1 CI is not evidence for this revision.
44
+
45
+ ## Real local acceptance
46
+
47
+ Environment: Windows, Magpie 0.1.1908, host Bun 1.4.4, existing unlocked local Respire Runtime (CLI reported 1.0.19-dev.3). Actual generation service: the user's existing local Magpie gateway with workbuddy-ai/deepseek-v4.1-flash. Raw capture disabled. A separate encrypted verification state directory isolated these probes.
48
+
49
+ | Probe | Observed evidence | Result |
50
+ | --- | --- | --- |
51
+ | Complete policy | Direct real-Runtime hook inspection returned the complete policy including its final anchor; injected block was about 59 KB | PASS |
52
+ | Ordinary turn E | No remember prefix and no new long-term fact; response was 收到。; detached worker wrote/read a trivial process diary, stored result, completed with 6 restricted calls | PASS |
53
+ | Explicit intent F | Foreground returned 待处理; worker wrote a new test fact under the requested existing parent, plus the process diary; both IDs were read back, completed with 12 restricted calls | PASS |
54
+ | Independent verification | Separate rsrs show confirmed F content and exact requested parent | PASS |
55
+ | Fresh recall F | Question contained only test code MGV-20261031-F, without expected answers; actual gateway returned 银灰色, 1648 and the same stored record ID | PASS |
56
+
57
+ Exact record IDs, response files and job receipts remain private in the ignored .verification/magpie-live directory; no credentials, full private memories or model reasoning are published here. Public CI is inspectable independently; these live observations are a maintainer report of a private installation.
58
+
59
+ The plugin was found disabled before live testing. It was temporarily enabled for the authorized acceptance, and its original disabled state and prior options are restored afterward. No Runtime/account lifecycle change or Magpie upgrade was performed.
60
+
61
+ ## Failed draft probes retained
62
+
63
+ - C: an earlier draft emitted a textual nonexistent tool call and wrote a diary, but archival ended unconfirmed. Complete-policy placement and host-execution instructions were corrected. It was not retried because a write had occurred.
64
+ - D: an earlier draft returned the requested short reply, but archival ended unconfirmed before any actual write. It was not replayed. Mandatory read-gate initialization was moved into host execution; subsequent independent E and F probes passed.
65
+ - The earlier drafts did not preserve a complete per-tool trace, so this report does not claim to have proven every cause of those failures. Their states and evidence remain available locally.
66
+
67
+ ## Not established
68
+
69
+ Before-send persistence; native desktop rendering; live Responses/Anthropic/Gemini sessions; production long-turn model processing; exhaustive crash/power-loss/account-switch/concurrent-load recovery; every update/merge branch; comparative agent-quality gains; npm publication or approved marketplace availability. See the [capability matrix](FEATURES.md).
@@ -0,0 +1,84 @@
1
+ # respire-memory v0.2.1 test report
2
+
3
+ Date: **2026-10-31**. Tested runtime code: [`0ac7162`](https://github.com/risense-ai/respire-magpie/commit/0ac7162), released as [v0.2.1](https://github.com/risense-ai/respire-magpie/releases/tag/v0.2.1). This report was written after the release; it does not claim the original release tarball includes these documentation files.
4
+
5
+ **Result:** the core recall → explicit pending receipt → detached archival → stored-record readback → fresh-request recall flow passed on the local installation. This does not mean all Codex host features are available, all protocol/client combinations were exercised live, or all failure paths were tested. See the [feature matrix](FEATURES.md).
6
+
7
+ ## 1. Automated verification
8
+
9
+ Public evidence:
10
+
11
+ - [CI for the tested source](https://github.com/risense-ai/respire-magpie/actions/runs/41183065419): passed.
12
+ - [Release verification and publication](https://github.com/risense-ai/respire-magpie/actions/runs/41183068079): passed; npm job intentionally skipped.
13
+ - [Existing check script at v0.2.1](https://github.com/risense-ai/respire-magpie/blob/v0.2.1/scripts/check.mjs).
14
+ - [CI matrix at v0.2.1](https://github.com/risense-ai/respire-magpie/blob/v0.2.1/.github/workflows/ci.yml): Ubuntu, Windows and macOS; Node 24 and Bun 1.3.6; package dry-run.
15
+
16
+ The checks use isolated state directories and simulated HTTP Runtime/generation services, while starting a real detached child worker. They do not use a production account or production generation model.
17
+
18
+ | Check | What the existing assertions establish |
19
+ | --- | --- |
20
+ | Four protocol shapes | Chat, Responses, Anthropic and Gemini request fixtures receive expected recall content without mutating the input object |
21
+ | Cache and caller boundary | Repeated request behavior is checked; gateway-key origin is skipped |
22
+ | Configuration / assets | Non-loopback or credential-bearing Runtime URLs are rejected; referenced files exist; embedded icon bytes match the SVG asset |
23
+ | Sensitive fields | Selected secret-shaped content/fields are rejected; this is not exhaustive detection of every secret format |
24
+ | Archival and readback | HTTP model tool loop stores a record, includes the real source digest, reads it back and completes the job |
25
+ | Encryption | Persisted job ciphertext does not contain the selected plaintext test fact |
26
+ | Unknown-write failure | Destroyed connection after a simulated write produces unconfirmed; processing that job again does not write again |
27
+ | Read-only and tool restrictions | Read-only worker does not write; ungrounded/force writes and policy-changing commands are rejected |
28
+ | Segmentation and sources | A 270,000-character user message splits; evidence reads are bounded; invented source references are rejected; segmented processing completes after evidence reads |
29
+ | Real child process | onResponse queues encrypted work; detached worker processes it through model tools and readback |
30
+ | Pending regression | `ok:false`, `exit:2`, envelope `status:pending` is returned as pending rather than rejected as invalid |
31
+
32
+ Reproduce from the complete plugin source directory:
33
+
34
+ ```sh
35
+ npm run check
36
+ bun scripts/check.mjs
37
+ npm pack --dry-run
38
+ ```
39
+
40
+ ## 2. Live local acceptance
41
+
42
+ Environment: Windows; **Magpie 0.1.1908**, Magpie-hosted **Bun 1.4.4**; an existing unlocked loopback Respire Runtime. Actual generation model: **workbuddy-ai/deepseek-v4.1-flash**, through the user's existing local Magpie gateway. No host or Runtime upgrade and no account change was performed.
43
+
44
+ | Scenario | Procedure and observed result | Status |
45
+ | --- | --- | --- |
46
+ | Existing-memory recall | Sent an actual local Chat Completions gateway request; model returned information and IDs from injected records | PASS |
47
+ | Explicit storage receipt | Submitted a non-sensitive test fact using the 记住: prefix; engine returned pending | PASS after fix below |
48
+ | Background resolution | Completed the reply; onResponse queued the pending turn; detached worker used the real configured model and completed archival in 7 tool calls | PASS |
49
+ | Actual persistence | Worker reported stored and checkedRecords with the same test record; a separate `rsrs show` confirmed the fact and parent | PASS |
50
+ | Fresh-request recall | Asked for the test color and check number without putting their expected values in the question; response returned 湖蓝色, 8426 and the stored record ID | PASS |
51
+ | Installed package | Installed the 0.2.1 package under the respire-memory directory, repeated the gateway recall successfully, then replaced the installed contents with the checksum-verified GitHub Release artifact | PASS; no additional live request claimed after the final identical-version artifact replacement |
52
+ | Names and introduction | Actual Magpie web sidebar, installed list, page heading and introduction showed respire-memory and the agent-experience description | PASS |
53
+ | Icon display | Inspected rendered light and dark Magpie web pages; both page images loaded, the active variant switched, and sidebar icon displayed | PASS |
54
+
55
+ Synthetic fact used: test code **MGV-20261031-B**, color **湖蓝色**, number **8426**. Record and turn IDs are omitted from this public report. Exact receipts, IDs, response files and the worker result remain in the local ignored `.verification/magpie-live/` directory, summarized in `result.json`; raw account data, private memory bodies, credentials and model reasoning are not published.
56
+
57
+ The public CI links are independently inspectable. The local acceptance observations are a maintainer's report, not publicly replayable proof of the private installation. A different installation must run its own live acceptance.
58
+
59
+ ## 3. Bug found and fixed during acceptance
60
+
61
+ Respire legitimately represents a pending write as RPC `ok:false`, process exit **2**, and an envelope with `status:pending`. The old RPC adapter required success/exit 0 and misclassified this as an unknown write.
62
+
63
+ v0.2.1 accepts exactly that pending combination. The front end now reports pending, and enabled background archival can resolve it under engine policy. Pending is not reported as saved; unknown writes remain non-retryable. The existing check script includes the regression assertion. The first uncertain test was not automatically retried.
64
+
65
+ ## 4. Release validation
66
+
67
+ The release workflow passed all three operating systems, published the tarball and SHA256SUMS, and skipped npm as configured. The downloaded tarball matched SHA256SUMS:
68
+
69
+ ```text
70
+ rsrsai-magpie-0.2.1.tgz
71
+ sha256 c15bcc10e4a0c4d5e499390f1cdfebf884ecbc7e82b720ce9b86e70d60007331
72
+ ```
73
+
74
+ Release delivery is separate from marketplace review. [Magpie community PR #553](https://github.com/magpie-community/plugins/pull/553) is the proposed registry/package integration; submission does not prove merge or listing availability.
75
+
76
+ ## 5. Not verified / not provided
77
+
78
+ - Native desktop window rendering; a forced Magpie theme that differs from the system theme.
79
+ - Live Responses, Anthropic and Gemini client sessions; exhaustive streaming, reconnect, malformed response and provider combinations.
80
+ - Live raw diary capture, every update/merge branch, real process-crash recovery, queue saturation, power loss, account-switch races or long-duration/concurrent stress.
81
+ - A production long-turn segmentation run through the real generation service. Automated segmentation coverage uses fixtures.
82
+ - Automatic client plan-mode detection, genuine SessionStart/resume/compaction callbacks and guaranteed native status lines: these are host capability gaps, not completed features.
83
+ - Exactly-once delivery before enqueue, reconstruction of unseen Responses history, image/audio/private-reasoning archival, or benchmarked agent-quality gains.
84
+ - npm publication and approved community marketplace delivery. Both require their separate publishing/review steps.
package/generation.mjs ADDED
@@ -0,0 +1,24 @@
1
+ import { hasSecret } from './runtime.mjs';
2
+
3
+ export async function generate(o, messages, tools, signal) {
4
+ if (!o.generationBaseUrl || !o.generationModel) throw new Error('generation-not-configured');
5
+ const url = new URL(o.generationBaseUrl);
6
+ const local = ['127.0.0.1', '[::1]'].includes(url.hostname);
7
+ if (url.href.includes('@') || url.search || url.hash || !(url.protocol === 'https:' || local && url.protocol === 'http:')) throw new Error('invalid-generation-url');
8
+ url.pathname = url.pathname.replace(/\/$/, '') + '/chat/completions';
9
+ const headers = { 'Content-Type': 'application/json', 'User-Agent': 'respire-memory' };
10
+ const token = process.env[o.authorizationEnv];
11
+ if (token) headers.Authorization = /^Bearer /i.test(token) ? token : `Bearer ${token}`;
12
+ const response = await fetch(url, { method: 'POST', redirect: 'error', signal: AbortSignal.any([signal, AbortSignal.timeout(60000)]), headers,
13
+ body: JSON.stringify({ model: o.generationModel, messages, stream: false, ...(tools?.length ? { tools, parallel_tool_calls: false } : {}), max_tokens: 8192 }) });
14
+ if (!response.ok) { await response.body?.cancel(); throw new Error('generation-unavailable'); }
15
+ const reader = response.body.getReader(); let size = 0; const chunks = [];
16
+ try {
17
+ while (true) { const { done, value } = await reader.read(); if (done) break; size += value.length;
18
+ if (size > 1024 * 1024) { await reader.cancel(); throw new Error('generation-output-limit'); } chunks.push(value); }
19
+ } finally { reader.releaseLock(); }
20
+ const data = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(Buffer.concat(chunks)));
21
+ const choice = data.choices?.[0];
22
+ if (!choice || !['stop', 'tool_calls'].includes(choice.finish_reason) || !choice.message || hasSecret(choice.message)) throw new Error('generation-invalid-output');
23
+ return choice.message;
24
+ }
package/lock.mjs ADDED
@@ -0,0 +1,86 @@
1
+ import { mkdir, readdir, writeFile, readFile, unlink, rmdir, lstat } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { randomBytes } from 'node:crypto';
4
+ import { setTimeout as delay } from 'node:timers/promises';
5
+
6
+ export function alive(pid) {
7
+ if (!Number.isSafeInteger(pid) || pid <= 0) return true;
8
+ try { process.kill(pid, 0); return true; }
9
+ catch (error) { if (error.code === 'ESRCH') return false; if (error.code === 'EPERM') return true; throw error; }
10
+ }
11
+
12
+ async function removeEmpty(path) {
13
+ for (let attempt = 0; ; attempt++) {
14
+ try { await rmdir(path); return true; }
15
+ catch (error) {
16
+ if (['ENOENT', 'ENOTEMPTY', 'EEXIST'].includes(error.code)) return false;
17
+ if (process.platform === 'win32' && ['EPERM', 'EBUSY'].includes(error.code)) {
18
+ // Windows can briefly retain a directory handle; an empty lock remains recoverable.
19
+ if (attempt === 2) return false;
20
+ await delay(25); continue;
21
+ }
22
+ throw error;
23
+ }
24
+ }
25
+ }
26
+
27
+ export async function acquireLock(path, { waitMs = 5000 } = {}) {
28
+ const deadline = Date.now() + Math.max(waitMs, 100);
29
+ let first = true;
30
+ while (true) {
31
+ if (!first && Date.now() >= deadline) throw new Error('lock-busy');
32
+ first = false;
33
+ try {
34
+ await mkdir(path, { mode: 0o700 });
35
+ const owner = `${process.pid}-${randomBytes(12).toString('hex')}.owner`;
36
+ try {
37
+ await writeFile(join(path, owner), String(Date.now()), { flag: 'wx', mode: 0o600 });
38
+ const owners = await readdir(path);
39
+ // An empty directory may be recovered while its creator is publishing ownership.
40
+ if (owners.length === 1 && owners[0] === owner) return async () => {
41
+ await unlink(join(path, owner)); await removeEmpty(path);
42
+ };
43
+ await unlink(join(path, owner));
44
+ } catch (error) {
45
+ // Concurrent parent removal reports EPERM on Windows and EINVAL on macOS Bun.
46
+ const racedOpen = (process.platform === 'win32' && error.code === 'EPERM')
47
+ || (process.platform === 'darwin' && process.versions.bun && error.code === 'EINVAL');
48
+ if (racedOpen && error.syscall === 'open' && error.path === join(path, owner)) {
49
+ if (Date.now() >= deadline) throw error;
50
+ await delay(25); continue;
51
+ }
52
+ if (error.code !== 'ENOENT') throw error;
53
+ }
54
+ } catch (error) {
55
+ if (error.code !== 'EEXIST') throw error;
56
+ try {
57
+ if ((await lstat(path)).isDirectory()) {
58
+ for (const name of await readdir(path)) {
59
+ const match = /^([1-9]\d*)-[a-f0-9]+\.owner$/.exec(name);
60
+ if (match && !alive(Number(match[1]))) {
61
+ try { await unlink(join(path, name)); } catch (e) { if (e.code !== 'ENOENT') throw e; }
62
+ }
63
+ }
64
+ if (await removeEmpty(path)) continue;
65
+ } else {
66
+ // Older workers used a PID file. Never unlink an unknown or live owner.
67
+ const pid = Number(await readFile(path, 'utf8'));
68
+ if (!alive(pid)) {
69
+ try { await unlink(path); } catch (e) { if (!['ENOENT', 'EISDIR', 'EPERM'].includes(e.code)) throw e; }
70
+ continue;
71
+ }
72
+ }
73
+ } catch (e) {
74
+ if (['ENOENT', 'EISDIR', 'ENOTDIR'].includes(e.code)) continue;
75
+ // A competing release can leave the lock directory delete-pending during a probe.
76
+ if (process.platform === 'win32' && e.code === 'EPERM' && e.syscall === 'scandir' && e.path === path) {
77
+ if (Date.now() >= deadline) throw e;
78
+ await delay(25); continue;
79
+ }
80
+ throw e;
81
+ }
82
+ }
83
+ if (waitMs === 0 || Date.now() >= deadline) throw new Error('lock-busy');
84
+ await delay(25);
85
+ }
86
+ }
@@ -0,0 +1,114 @@
1
+ // Reused from Respire Codex's restricted archival tools.
2
+ import { containsSecret, validId } from './runtime.mjs';
3
+ const record = value => value && typeof value === 'object' && !Array.isArray(value);
4
+ const READ = new Set(['recall', 'show', 'chain', 'prompt', 'tree', 'taxonomy', 'agent-config', 'query-log', 'diary-search', 'diary-fragment'])
5
+ const WRITE = new Set(['remember', 'update'])
6
+ export const TOOL = {
7
+ type: 'function', name: 'respire_memory',
8
+ description: 'Run rsrs memory commands. args are CLI arguments without --client-only or --json. Read policy with prompt, recall titles and show candidates before writing. Use update for the same subject, or remember with --title and --parent for a new important fact. Diary reads are bounded historical references, not verified evidence. Use only for history or insufficient/conflicting main memories. Unsupported Runtime commands must not trigger whole-diary fallback. No force, merge, credential, runtime or configuration changes.',
9
+ inputSchema: { type: 'object', properties: { command: { type: 'string', enum: [...READ, ...WRITE] }, args: { type: 'array', items: { type: 'string' } } }, required: ['command', 'args'], additionalProperties: false },
10
+ }
11
+
12
+ // 日记检索只开放固定预算的只读参数,不能由模型扩大成整库正文读取。
13
+ function boundedDiaryArgs(command, args) {
14
+ const search = command === 'diary-search'
15
+ const positional = search ? 1 : 2
16
+ if (args.length < positional || args.slice(0, positional).some(arg => !arg.trim() || arg.startsWith('-'))) throw new Error('bounded-diary-arguments')
17
+ if (search && [...args[0]].length > 500) throw new Error('bounded-diary-query')
18
+ if (!search && !validId(args[0])) throw new Error('bounded-diary-source')
19
+ const options = search
20
+ ? new Map([['--limit','5'],['--max-chars','4000'],['--mode','hybrid']])
21
+ : new Map([['--neighbors','0'],['--max-chars','4000']])
22
+ const seen = new Set()
23
+ for (let i = positional; i < args.length; i += 2) {
24
+ const key = args[i], value = args[i + 1]
25
+ if (!options.has(key) || seen.has(key) || value === undefined) throw new Error('bounded-diary-option')
26
+ if (key === '--mode' ? !['lexical','hybrid'].includes(value)
27
+ : !/^\d+$/.test(value) || Number(value) > Number(key === '--neighbors' ? 1 : options.get(key)) || Number(value) < (key === '--neighbors' ? 0 : key === '--max-chars' ? 512 : 1)) throw new Error('bounded-diary-budget')
28
+ options.set(key, value); seen.add(key)
29
+ }
30
+ return [...args.slice(0, positional), ...[...options].flat()]
31
+ }
32
+
33
+ export function memoryTools(call, { readOnly = false } = {}) {
34
+ let recalled = false
35
+ let policy = false
36
+ let uncertain = false
37
+ let diaryUnavailable = false
38
+ const written = new Set()
39
+ const diaries = new Set()
40
+ const shown = new Set()
41
+ const verified = new Set()
42
+ const commands = []
43
+ const attempts = new Set()
44
+ return {
45
+ written, shown, commands,
46
+ get uncertain() { return uncertain },
47
+ async run(input) {
48
+ if (!record(input) || ![...READ, ...WRITE].includes(input.command) || !Array.isArray(input.args) || input.args.some(arg => typeof arg !== 'string') || JSON.stringify(input).length > 16000) throw new Error('invalid-memory-call')
49
+ const { command } = input
50
+ let args = input.args.filter(arg => arg !== '--json' && arg !== '--client-only')
51
+ if (args.some(arg => /^(?:--(?:direct|force|merge-ids|go|ensure|set|addr|data-dir))(?:=|$)/.test(arg))) throw new Error('forbidden-memory-option')
52
+ if (command === 'taxonomy' && (args.length !== 1 || args[0] !== '--list')) throw new Error('read-only-taxonomy')
53
+ if (command === 'agent-config' && args.length) throw new Error('read-only-config')
54
+ const diary = command === 'diary-search' || command === 'diary-fragment'
55
+ if (diary && diaryUnavailable) throw new Error('diary-unsupported')
56
+ if (diary) args = boundedDiaryArgs(command, args)
57
+ const write = WRITE.has(command)
58
+ if (write) {
59
+ if (readOnly) throw new Error('read-only')
60
+ if (uncertain || !recalled || !policy) throw new Error('read-before-write')
61
+ if (containsSecret(JSON.stringify(input))) throw new Error('secret-content')
62
+ const trivial = command === 'remember' && args.includes('--importance') && args[args.indexOf('--importance') + 1] === 'trivial'
63
+ const title = args.indexOf('--title')
64
+ if (!trivial && (title < 0 || !args[title + 1]?.trim())) throw new Error('title-required')
65
+ if (command === 'remember' && !trivial) {
66
+ const parent = args[args.indexOf('--parent') + 1]
67
+ if (!args.includes('--parent') || !validId(parent) || !shown.has(parent)) throw new Error('shown-parent-required')
68
+ }
69
+ if (command === 'update' && (!validId(args[0]) || !shown.has(args[0]))) throw new Error('show-before-update')
70
+ const key = JSON.stringify([command, args])
71
+ if (attempts.has(key)) throw new Error('duplicate-write')
72
+ attempts.add(key)
73
+ uncertain = true
74
+ }
75
+ commands.push(command)
76
+ let doc
77
+ try { doc = await call([command, ...args]) }
78
+ catch (error) {
79
+ if (diary && error.message === 'diary-unsupported') diaryUnavailable = true
80
+ throw error
81
+ }
82
+ if (diary && JSON.stringify(doc).length > 8000) throw new Error('diary-output-too-large')
83
+ if (command === 'recall' && ['ok', 'skip'].includes(doc.status)) recalled = true
84
+ if (command === 'prompt') {
85
+ if (doc.status !== 'ok' || typeof doc.summary?.instructions !== 'string' || !doc.summary.instructions.trim()) throw new Error('invalid-engine-policy')
86
+ if (recalled) policy = true
87
+ }
88
+ if (command === 'show' && doc.status === 'ok' && validId(doc.summary?.id)) {
89
+ if (!validId(args[0]) || !doc.summary.id.startsWith(args[0])) throw new Error('invalid-readback')
90
+ shown.add(doc.summary.id)
91
+ shown.add(args[0])
92
+ if (written.has(doc.summary.id)) verified.add(doc.summary.id)
93
+ }
94
+ if (write) {
95
+ if (doc.status === 'pending' || doc.status === 'skip') uncertain = false
96
+ else if (doc.status === 'ok' && validId(doc.summary?.id)) {
97
+ uncertain = false; written.add(doc.summary.id); verified.delete(doc.summary.id)
98
+ if (command === 'remember' && args.includes('--importance') && args[args.indexOf('--importance') + 1] === 'trivial') diaries.add(doc.summary.id)
99
+ }
100
+ else throw new Error('write-unconfirmed')
101
+ }
102
+ return doc
103
+ },
104
+ finish(text) {
105
+ if (uncertain || !recalled || !policy) throw new Error('archive-unconfirmed')
106
+ let receipt
107
+ try { receipt = JSON.parse(text) } catch { throw new Error('archive-invalid-receipt') }
108
+ if (!['stored', 'already-stored', 'nothing-to-store', 'read-only'].includes(receipt.result) || !Array.isArray(receipt.ids) || receipt.ids.some(id => !validId(id) || !shown.has(id))) throw new Error('archive-invalid-receipt')
109
+ if ([...written].some(id => !verified.has(id)) || (receipt.result === 'stored' && !written.size) || (receipt.result === 'already-stored' && !receipt.ids.length)) throw new Error('archive-unverified')
110
+ if (!readOnly && ![...diaries].some(id => verified.has(id))) throw new Error('turn-diary-required')
111
+ return { result: written.size ? 'stored' : receipt.result, records: [...written], checkedRecords: [...new Set([...receipt.ids, ...verified])], calls: commands.length }
112
+ },
113
+ }
114
+ }
package/package.json CHANGED
@@ -1,6 +1,77 @@
1
1
  {
2
2
  "name": "@rsrsai/magpie",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.3.3",
4
+ "description": "帮助 AI Agent 通过记忆召回、经验积累与后台归档持续自进化",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "magpie-plugin",
9
+ "magpie",
10
+ "respire",
11
+ "memory"
12
+ ],
13
+ "files": [
14
+ "respire.hooks.mjs",
15
+ "runtime.mjs",
16
+ "lock.mjs",
17
+ "conversation.mjs",
18
+ "page",
19
+ "README.md",
20
+ "docs",
21
+ "LICENSE",
22
+ "NOTICE",
23
+ "state.mjs",
24
+ "queue.mjs",
25
+ "worker.mjs",
26
+ "archive.mjs",
27
+ "archive-context.mjs",
28
+ "generation.mjs",
29
+ "memory-tools.mjs",
30
+ "policy.mjs"
31
+ ],
32
+ "engines": {
33
+ "bun": ">=1.3.6"
34
+ },
35
+ "magpie": {
36
+ "hooks": "./respire.hooks.mjs",
37
+ "options": {
38
+ "runtimeUrl": "http://127.0.0.1:15169",
39
+ "project": "",
40
+ "gatewayTimeoutMs": 5000,
41
+ "requestTimeoutMs": 1500,
42
+ "maxBytes": 8000,
43
+ "limit": 3,
44
+ "remember": true,
45
+ "capture": false,
46
+ "readOnly": false,
47
+ "archive": true,
48
+ "showMemoryStatus": false,
49
+ "injectRules": true,
50
+ "generationBaseUrl": "",
51
+ "generationModel": "",
52
+ "authorizationEnv": "RESPIRE_MAGPIE_AUTHORIZATION",
53
+ "stateDir": ""
54
+ },
55
+ "page": {
56
+ "title": "respire-memory",
57
+ "file": "page/settings.html",
58
+ "icon": "page/icon.svg"
59
+ },
60
+ "icon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj4KICA8IS0tIFJlc3BpcmUgQUkgbWVtb3J5IGljb246IGxpZ2h0IHZhcmlhbnQgYnkgZGVmYXVsdCwgZGFyayB2YXJpYW50IHVuZGVyIGRhcmsgY29sb3Igc2NoZW1lLiAtLT4KICA8c3R5bGU+CiAgICAucmVzcGlyZS1pY29uLWRhcmsgeyBkaXNwbGF5OiBub25lIH0KICAgIEBtZWRpYSAocHJlZmVycy1jb2xvci1zY2hlbWU6IGRhcmspIHsKICAgICAgLnJlc3BpcmUtaWNvbi1saWdodCB7IGRpc3BsYXk6IG5vbmUgfQogICAgICAucmVzcGlyZS1pY29uLWRhcmsgeyBkaXNwbGF5OiBpbmxpbmUgfQogICAgfQogIDwvc3R5bGU+CiAgPGRlZnM+CiAgICA8IS0tIExpZ2h0IHZhcmlhbnQgKGwtIHByZWZpeCk6IHdhcm0gcGFwZXIgYmFzZSwgd2FybSBpbmsgbGluZXMuIC0tPgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJsLWJnIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjAlIiB5Mj0iMTAwJSI+CiAgICAgIDxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNGQUY3RjAiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI0VGRUFFMCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8cmFkaWFsR3JhZGllbnQgaWQ9ImwtYnJlYXRoIiBjeD0iNTAlIiBjeT0iNTIlIiByPSI0MiUiPgogICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjQTkzMDIxIiBzdG9wLW9wYWNpdHk9IjAuMDgiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iNjUlIiBzdG9wLWNvbG9yPSIjQTkzMDIxIiBzdG9wLW9wYWNpdHk9IjAuMDIiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI0E5MzAyMSIgc3RvcC1vcGFjaXR5PSIwIiAvPgogICAgPC9yYWRpYWxHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0ibC1pbmsiIHgxPSIwJSIgeTE9IjUwJSIgeDI9IjEwMCUiIHkyPSI1MCUiPgogICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjMjExRTE5IiBzdG9wLW9wYWNpdHk9IjAuMzAiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iMTUlIiBzdG9wLWNvbG9yPSIjMjExRTE5IiBzdG9wLW9wYWNpdHk9IjAuOTIiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iODUlIiBzdG9wLWNvbG9yPSIjMjExRTE5IiBzdG9wLW9wYWNpdHk9IjAuOTIiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzIxMUUxOSIgc3RvcC1vcGFjaXR5PSIwLjQwIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0ibC1ydXN0IiB4MT0iMCUiIHkxPSI1MCUiIHgyPSIxMDAlIiB5Mj0iNTAlIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI0E5MzAyMSIgc3RvcC1vcGFjaXR5PSIwLjQiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iMTglIiBzdG9wLWNvbG9yPSIjQTkzMDIxIiBzdG9wLW9wYWNpdHk9IjEiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iNTIlIiBzdG9wLWNvbG9yPSIjRDE0MzMyIiBzdG9wLW9wYWNpdHk9IjEiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iODUlIiBzdG9wLWNvbG9yPSIjQTkzMDIxIiBzdG9wLW9wYWNpdHk9IjEiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI0E5MzAyMSIgc3RvcC1vcGFjaXR5PSIwLjQ1IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0ibC1jaGlwIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjAlIiB5Mj0iMTAwJSI+CiAgICAgIDxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNGRkZERjgiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI0Y2RjJFOSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8ZmlsdGVyIGlkPSJsLWxpZnQiIHg9Ii0xMCUiIHk9Ii0yMCUiIHdpZHRoPSIxMjAlIiBoZWlnaHQ9IjE0MCUiPgogICAgICA8ZmVEcm9wU2hhZG93IGR4PSIwIiBkeT0iMyIgc3RkRGV2aWF0aW9uPSI1IiBmbG9vZC1jb2xvcj0iIzNBMzMyQSIgZmxvb2Qtb3BhY2l0eT0iMC4yMiIgLz4KICAgIDwvZmlsdGVyPgogICAgPHBhdGggaWQ9Imwtc3BhcmsiIGQ9Ik0wIC0yNiBRNCAtNiAyNCAwIFE0IDYgMCAyNiBRLTQgNiAtMjQgMCBRLTQgLTYgMCAtMjYgWiIgLz4KICAgIDwhLS0gRGFyayB2YXJpYW50IChkLSBwcmVmaXgpOiB3YXJtIG9ic2lkaWFuIGJhc2UsIHBhcmNobWVudCBsaW5lcy4gLS0+CiAgICA8bGluZWFyR3JhZGllbnQgaWQ9ImQtYmciIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMCUiIHkyPSIxMDAlIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzE0MTQxMyIgLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMEEwQTA5IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxyYWRpYWxHcmFkaWVudCBpZD0iZC1icmVhdGgiIGN4PSI1MCUiIGN5PSI1MiUiIHI9IjQyJSI+CiAgICAgIDxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNBOTMwMjEiIHN0b3Atb3BhY2l0eT0iMC4xNCIgLz4KICAgICAgPHN0b3Agb2Zmc2V0PSI2NSUiIHN0b3AtY29sb3I9IiNBOTMwMjEiIHN0b3Atb3BhY2l0eT0iMC4wMyIgLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMEEwQTA5IiBzdG9wLW9wYWNpdHk9IjAiIC8+CiAgICA8L3JhZGlhbEdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJkLWxpbmUiIHgxPSIwJSIgeTE9IjUwJSIgeDI9IjEwMCUiIHkyPSI1MCUiPgogICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjRjVGMkVCIiBzdG9wLW9wYWNpdHk9IjAuMzUiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iMTUlIiBzdG9wLWNvbG9yPSIjRjVGMkVCIiBzdG9wLW9wYWNpdHk9IjAuOTgiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iODUlIiBzdG9wLWNvbG9yPSIjRjVGMkVCIiBzdG9wLW9wYWNpdHk9IjAuOTgiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI0Y1RjJFQiIgc3RvcC1vcGFjaXR5PSIwLjQ1IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iZC1ydXN0IiB4MT0iMCUiIHkxPSI1MCUiIHgyPSIxMDAlIiB5Mj0iNTAlIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI0E5MzAyMSIgc3RvcC1vcGFjaXR5PSIwLjQiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iMTglIiBzdG9wLWNvbG9yPSIjQTkzMDIxIiBzdG9wLW9wYWNpdHk9IjEiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iNTIlIiBzdG9wLWNvbG9yPSIjRDE0MzMyIiBzdG9wLW9wYWNpdHk9IjEiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iODUlIiBzdG9wLWNvbG9yPSIjQTkzMDIxIiBzdG9wLW9wYWNpdHk9IjEiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI0E5MzAyMSIgc3RvcC1vcGFjaXR5PSIwLjQ1IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxmaWx0ZXIgaWQ9ImQtbGlmdCIgeD0iLTEwJSIgeT0iLTIwJSIgd2lkdGg9IjEyMCUiIGhlaWdodD0iMTQwJSI+CiAgICAgIDxmZURyb3BTaGFkb3cgZHg9IjAiIGR5PSI0IiBzdGREZXZpYXRpb249IjYiIGZsb29kLWNvbG9yPSIjMDAwMDAwIiBmbG9vZC1vcGFjaXR5PSIwLjYiIC8+CiAgICA8L2ZpbHRlcj4KICAgIDxwYXRoIGlkPSJkLXNwYXJrIiBkPSJNMCAtMjYgUTQgLTYgMjQgMCBRNCA2IDAgMjYgUS00IDYgLTI0IDAgUS00IC02IDAgLTI2IFoiIC8+CiAgPC9kZWZzPgoKICA8IS0tIExpZ2h0IHZhcmlhbnQgYm9keSAtLT4KICA8ZyBjbGFzcz0icmVzcGlyZS1pY29uLWxpZ2h0Ij4KICAgIDxyZWN0IHg9IjI0IiB5PSIyNCIgd2lkdGg9IjQ2NCIgaGVpZ2h0PSI0NjQiIHJ4PSIxMDgiIGZpbGw9InVybCgjbC1iZykiIC8+CiAgICA8cmVjdCB4PSIyNCIgeT0iMjQiIHdpZHRoPSI0NjQiIGhlaWdodD0iNDY0IiByeD0iMTA4IiBmaWxsPSJ1cmwoI2wtYnJlYXRoKSIgLz4KICAgIDxyZWN0IHg9IjI0Ljc1IiB5PSIyNC43NSIgd2lkdGg9IjQ2Mi41IiBoZWlnaHQ9IjQ2Mi41IiByeD0iMTA3LjI1IiBmaWxsPSJub25lIiBzdHJva2U9IiMyMTFFMTkiIHN0cm9rZS1vcGFjaXR5PSIwLjEwIiBzdHJva2Utd2lkdGg9IjEuNSIgLz4KICAgIDxnIGZpbGw9Im5vbmUiIHN0cm9rZT0idXJsKCNsLXJ1c3QpIiBzdHJva2Utd2lkdGg9IjQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPgogICAgICA8cmVjdCB4PSIxMTAiIHk9IjEzNCIgd2lkdGg9IjMyMCIgaGVpZ2h0PSIyNzIiIHJ4PSI0OCIgc3Ryb2tlLW9wYWNpdHk9IjAuMzUiIC8+CiAgICAgIDxyZWN0IHg9IjEyNCIgeT0iMTQ4IiB3aWR0aD0iMzIwIiBoZWlnaHQ9IjI3MiIgcng9IjQ4IiBzdHJva2Utb3BhY2l0eT0iMC4xOCIgLz4KICAgIDwvZz4KICAgIDxyZWN0IHg9Ijk2IiB5PSIxMjAiIHdpZHRoPSIzMjAiIGhlaWdodD0iMjcyIiByeD0iNDgiIGZpbGw9InVybCgjbC1jaGlwKSIgLz4KICAgIDxyZWN0IHg9Ijk2IiB5PSIxMjAiIHdpZHRoPSIzMjAiIGhlaWdodD0iMjcyIiByeD0iNDgiIGZpbGw9InVybCgjbC1icmVhdGgpIiAvPgogICAgPHJlY3QgeD0iOTYiIHk9IjEyMCIgd2lkdGg9IjMyMCIgaGVpZ2h0PSIyNzIiIHJ4PSI0OCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJ1cmwoI2wtaW5rKSIgc3Ryb2tlLXdpZHRoPSI1IiAvPgogICAgPGcgc3Ryb2tlPSIjMjExRTE5IiBzdHJva2Utb3BhY2l0eT0iMC43NSIgc3Ryb2tlLXdpZHRoPSI1IiBzdHJva2UtbGluZWNhcD0icm91bmQiPgogICAgICA8cGF0aCBkPSJNMTc2IDEyMFY5NiBNMjU2IDEyMFY5NiBNMzM2IDEyMFY5NiIgLz4KICAgICAgPHBhdGggZD0iTTE3NiAzOTJ2MjQgTTI1NiAzOTJ2MjQgTTMzNiAzOTJ2MjQiIC8+CiAgICAgIDxwYXRoIGQ9Ik05NiAxODRINzIgTTk2IDI1Nkg3MiBNOTYgMzI4SDcyIiAvPgogICAgICA8cGF0aCBkPSJNNDE2IDE4NGgyNCBNNDE2IDI1NmgyNCBNNDE2IDMyOGgyNCIgLz4KICAgIDwvZz4KICAgIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDY4LjgsIDk3LjYpIHNjYWxlKDcuMikiIGZpbHRlcj0idXJsKCNsLWxpZnQpIj4KICAgICAgPHBhdGggZD0iTTMgMjVDOSAxNCAxMyAxMCAxOSAxNHMxMSAyNCAyMCAyMGM1LTIgNy02IDEwLTkiIGZpbGw9Im5vbmUiIHN0cm9rZT0idXJsKCNsLXJ1c3QpIiBzdHJva2Utd2lkdGg9IjIuMSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiAvPgogICAgICA8cGF0aCBkPSJNMyAyM0MxMCAyMiA5IDYgMTggN1MzMiA0MCA0MSAzNmM0LTIgNi04IDgtMTIiIGZpbGw9Im5vbmUiIHN0cm9rZT0idXJsKCNsLWluaykiIHN0cm9rZS13aWR0aD0iMi41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIC8+CiAgICA8L2c+CiAgICA8ZyBmaWxsPSIjMjExRTE5Ij4KICAgICAgPHJlY3QgeD0iMTMyIiB5PSIzMTgiIHdpZHRoPSI2OCIgaGVpZ2h0PSIxMCIgcng9IjUiIGZpbGwtb3BhY2l0eT0iMC41NSIgLz4KICAgICAgPHJlY3QgeD0iMTMyIiB5PSIzMzYiIHdpZHRoPSI1MiIgaGVpZ2h0PSIxMCIgcng9IjUiIGZpbGwtb3BhY2l0eT0iMC4zOCIgLz4KICAgICAgPHJlY3QgeD0iMTMyIiB5PSIzNTQiIHdpZHRoPSIzNiIgaGVpZ2h0PSIxMCIgcng9IjUiIGZpbGwtb3BhY2l0eT0iMC4yMiIgLz4KICAgIDwvZz4KICAgIDx1c2UgaHJlZj0iI2wtc3BhcmsiIHg9IjM3MCIgeT0iMTY0IiBmaWxsPSJ1cmwoI2wtcnVzdCkiIC8+CiAgICA8dXNlIGhyZWY9IiNsLXNwYXJrIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNDIgMTcwKSBzY2FsZSgwLjUpIiBmaWxsPSIjMjExRTE5IiBmaWxsLW9wYWNpdHk9IjAuNjgiIC8+CiAgPC9nPgoKICA8IS0tIERhcmsgdmFyaWFudCBib2R5IC0tPgogIDxnIGNsYXNzPSJyZXNwaXJlLWljb24tZGFyayI+CiAgICA8cmVjdCB4PSIyNCIgeT0iMjQiIHdpZHRoPSI0NjQiIGhlaWdodD0iNDY0IiByeD0iMTA4IiBmaWxsPSJ1cmwoI2QtYmcpIiAvPgogICAgPHJlY3QgeD0iMjQiIHk9IjI0IiB3aWR0aD0iNDY0IiBoZWlnaHQ9IjQ2NCIgcng9IjEwOCIgZmlsbD0idXJsKCNkLWJyZWF0aCkiIC8+CiAgICA8cmVjdCB4PSIyNC43NSIgeT0iMjQuNzUiIHdpZHRoPSI0NjIuNSIgaGVpZ2h0PSI0NjIuNSIgcng9IjEwNy4yNSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjRjVGMkVCIiBzdHJva2Utb3BhY2l0eT0iMC4wNyIgc3Ryb2tlLXdpZHRoPSIxLjUiIC8+CiAgICA8ZyBmaWxsPSJub25lIiBzdHJva2U9InVybCgjZC1ydXN0KSIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIj4KICAgICAgPHJlY3QgeD0iMTEwIiB5PSIxMzQiIHdpZHRoPSIzMjAiIGhlaWdodD0iMjcyIiByeD0iNDgiIHN0cm9rZS1vcGFjaXR5PSIwLjMwIiAvPgogICAgICA8cmVjdCB4PSIxMjQiIHk9IjE0OCIgd2lkdGg9IjMyMCIgaGVpZ2h0PSIyNzIiIHJ4PSI0OCIgc3Ryb2tlLW9wYWNpdHk9IjAuMTQiIC8+CiAgICA8L2c+CiAgICA8cmVjdCB4PSI5NiIgeT0iMTIwIiB3aWR0aD0iMzIwIiBoZWlnaHQ9IjI3MiIgcng9IjQ4IiBmaWxsPSJ1cmwoI2QtYmcpIiAvPgogICAgPHJlY3QgeD0iOTYiIHk9IjEyMCIgd2lkdGg9IjMyMCIgaGVpZ2h0PSIyNzIiIHJ4PSI0OCIgZmlsbD0idXJsKCNkLWJyZWF0aCkiIC8+CiAgICA8cmVjdCB4PSI5NiIgeT0iMTIwIiB3aWR0aD0iMzIwIiBoZWlnaHQ9IjI3MiIgcng9IjQ4IiBmaWxsPSJub25lIiBzdHJva2U9InVybCgjZC1saW5lKSIgc3Ryb2tlLXdpZHRoPSI1IiAvPgogICAgPGcgc3Ryb2tlPSIjRjVGMkVCIiBzdHJva2Utb3BhY2l0eT0iMC44IiBzdHJva2Utd2lkdGg9IjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCI+CiAgICAgIDxwYXRoIGQ9Ik0xNzYgMTIwVjk2IE0yNTYgMTIwVjk2IE0zMzYgMTIwVjk2IiAvPgogICAgICA8cGF0aCBkPSJNMTc2IDM5MnYyNCBNMjU2IDM5MnYyNCBNMzM2IDM5MnYyNCIgLz4KICAgICAgPHBhdGggZD0iTTk2IDE4NEg3MiBNOTYgMjU2SDcyIE05NiAzMjhINzIiIC8+CiAgICAgIDxwYXRoIGQ9Ik00MTYgMTg0aDI0IE00MTYgMjU2aDI0IE00MTYgMzI4aDI0IiAvPgogICAgPC9nPgogICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjguOCwgOTcuNikgc2NhbGUoNy4yKSIgZmlsdGVyPSJ1cmwoI2QtbGlmdCkiPgogICAgICA8cGF0aCBkPSJNMyAyNUM5IDE0IDEzIDEwIDE5IDE0czExIDI0IDIwIDIwYzUtMiA3LTYgMTAtOSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJ1cmwoI2QtcnVzdCkiIHN0cm9rZS13aWR0aD0iMi4xIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIC8+CiAgICAgIDxwYXRoIGQ9Ik0zIDIzQzEwIDIyIDkgNiAxOCA3UzMyIDQwIDQxIDM2YzQtMiA2LTggOC0xMiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJ1cmwoI2QtbGluZSkiIHN0cm9rZS13aWR0aD0iMi41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIC8+CiAgICA8L2c+CiAgICA8ZyBmaWxsPSIjRjVGMkVCIj4KICAgICAgPHJlY3QgeD0iMTMyIiB5PSIzMTgiIHdpZHRoPSI2OCIgaGVpZ2h0PSIxMCIgcng9IjUiIGZpbGwtb3BhY2l0eT0iMC42MCIgLz4KICAgICAgPHJlY3QgeD0iMTMyIiB5PSIzMzYiIHdpZHRoPSI1MiIgaGVpZ2h0PSIxMCIgcng9IjUiIGZpbGwtb3BhY2l0eT0iMC40MiIgLz4KICAgICAgPHJlY3QgeD0iMTMyIiB5PSIzNTQiIHdpZHRoPSIzNiIgaGVpZ2h0PSIxMCIgcng9IjUiIGZpbGwtb3BhY2l0eT0iMC4yNiIgLz4KICAgIDwvZz4KICAgIDx1c2UgaHJlZj0iI2Qtc3BhcmsiIHg9IjM3MCIgeT0iMTY0IiBmaWxsPSJ1cmwoI2QtcnVzdCkiIC8+CiAgICA8dXNlIGhyZWY9IiNkLXNwYXJrIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNDIgMTcwKSBzY2FsZSgwLjUpIiBmaWxsPSIjRjVGMkVCIiBmaWxsLW9wYWNpdHk9IjAuNzUiIG9wYWNpdHk9IjAuOSIgLz4KICA8L2c+Cjwvc3ZnPgo="
61
+ },
62
+ "repository": {
63
+ "type": "git",
64
+ "url": "git+https://github.com/risense-ai/respire-magpie.git"
65
+ },
66
+ "homepage": "https://github.com/risense-ai/respire-magpie#readme",
67
+ "bugs": {
68
+ "url": "https://github.com/risense-ai/respire-magpie/issues"
69
+ },
70
+ "publishConfig": {
71
+ "access": "public"
72
+ },
73
+ "scripts": {
74
+ "check": "node scripts/check.mjs",
75
+ "release:package": "node scripts/release.mjs"
76
+ }
77
+ }