diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..4e6548c
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,51 @@
+# Code of Conduct
+
+Meldwork is a workspace for people and Agents to build together. We want issues,
+pull requests, discussions, and community spaces to stay useful, direct, and
+respectful.
+
+## Expected Behavior
+
+- Be clear, constructive, and respectful when giving feedback.
+- Assume good intent while keeping discussions focused on facts, tradeoffs, and outcomes.
+- Respect different experience levels, backgrounds, languages, and working styles.
+- Keep disagreements technical, specific, and connected to the project.
+- Help maintainers keep issues and pull requests focused.
+- Protect private prompts, credentials, customer information, and other sensitive data.
+
+## Unacceptable Behavior
+
+The following behavior is not acceptable in Meldwork project spaces:
+
+- Personal attacks, insults, harassment, or sustained disruption.
+- Demeaning comments about identity, background, language, ability, or experience.
+- Threats, intimidation, stalking, or unwanted contact.
+- Publishing another person's private information without consent.
+- Spam, trolling, discrimination, or repeated off-topic posting.
+- Submitting malicious code, secrets, private data, or intentionally misleading reports.
+- Any behavior maintainers reasonably judge to make the project unsafe or unproductive.
+
+## Maintainer Responsibilities
+
+Maintainers may edit or remove comments, close issues, limit participation, or
+block participants when needed to protect the project community. Enforcement
+should be proportional, documented when practical, and focused on keeping
+collaboration healthy.
+
+## Reporting
+
+Report conduct concerns privately through the repository's GitHub contact or
+security reporting channel. Do not open a public issue for sensitive reports.
+
+Please include:
+
+- a short description of what happened;
+- links, screenshots, or message references when available;
+- whether you want follow-up, confidentiality, or no direct reply.
+
+Reports will be reviewed by project maintainers. We will handle reports with
+care and limit access to people who need the information to respond.
+
+This Code of Conduct applies wherever the project or its maintainers represent
+the Meldwork community. It is adapted from the Contributor Covenant and the
+community standards used by comparable open-source projects.
diff --git a/COMMERCIAL_USE.md b/COMMERCIAL_USE.md
index 3f7a6d7..cc9c93d 100644
--- a/COMMERCIAL_USE.md
+++ b/COMMERCIAL_USE.md
@@ -1,17 +1,14 @@
# Commercial Use And Licensing
-Meldwork is source-available under the [Meldwork Non-Commercial Source License](LICENSE). The public repository can be inspected, evaluated, run, and modified for non-commercial purposes, but it is not licensed for commercial use.
+Meldwork is licensed under the [Apache License 2.0](LICENSE). Commercial use,
+private use, modification, redistribution, and use inside a paid service are
+permitted under the license terms.
-Any commercial use requires prior written permission from the copyright holder. This includes monetizing Meldwork, reselling or hosting it, embedding it in a proprietary product, using it to provide paid services, or using it as part of a for-profit organization's business operations.
+This page is a practical summary and does not replace the license text. Apache
+License 2.0 does not grant trademark rights to the Meldwork name, logo,
+screenshots, or other brand assets. Third-party software, Agent products,
+models, names, logos, and other materials remain subject to their own licenses
+and terms.
-The copyright holder may offer a separate commercial license for organizations that need permission for:
-
-- proprietary redistribution or resale of Meldwork or a modified version;
-- embedding Meldwork code in a closed-source product;
-- offering a hosted, managed, or network-accessible version;
-- internal business operations at a for-profit organization;
-- separately licensed commercial features, enterprise integrations, support, or managed deployment.
-
-A commercial license does not change the licenses of third-party dependencies, Agent products, names, models, or logos. The repository license also does not grant trademark rights to the Meldwork name, logo, screenshots, or other brand assets.
-
-This page is a practical summary and does not replace the license text or legal advice. When a planned use may be commercial, contact the repository owner through GitHub before using, distributing, hosting, reselling, or integrating the product.
+Before distributing a product that includes third-party materials, review the
+relevant notices and preserve the required attribution and license information.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7d8e055..29fbf2c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,8 +1,8 @@
# Contributing
-Meldwork is source-available under the [Meldwork Non-Commercial Source License](LICENSE). Contributions are welcome when they improve the local-first desktop product, documentation, tests, or security posture.
+Meldwork is licensed under the [Apache License 2.0](LICENSE). Contributions are welcome when they improve the local-first desktop product, documentation, tests, or security posture.
-Unless a separate written agreement applies, a contribution accepted into this repository is available under the repository license. The project may also offer separately licensed commercial editions. Before external code is incorporated into a separately licensed commercial build, the maintainer may require a reviewed contributor-license agreement that grants the necessary relicensing rights. Opening a pull request by itself does not transfer copyright or silently grant those additional rights.
+Unless a separate written agreement applies, a contribution accepted into this repository is available under the repository license. Opening a pull request does not transfer copyright beyond the rights granted by the Apache License 2.0.
## Ground Rules
@@ -11,8 +11,9 @@ Unless a separate written agreement applies, a contribution accepted into this r
- Do not add remote services, telemetry, credential collection, or server dependencies without explicit project approval.
- Preserve the local-first boundary: renderer code must not gain unrestricted filesystem, shell, Provider credential, or executable-path access.
- Include focused tests for behavior, security boundary, storage, or IPC changes.
-- Confirm that all submitted code and assets are yours to license and do not introduce incompatible license obligations.
+- Confirm that all submitted code and assets are yours to contribute and do not introduce incompatible license obligations.
-## Commercial Editions
+## Community Standards
-Commercial use of Meldwork requires prior written permission. Commercial editions, proprietary integrations, support, and other paid offerings may use separate terms; see [COMMERCIAL_USE.md](COMMERCIAL_USE.md).
+Please follow the [Code of Conduct](CODE_OF_CONDUCT.md) in issues, pull
+requests, discussions, and other project spaces.
diff --git a/LICENSE b/LICENSE
index 763b2de..261eeb9 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,82 +1,201 @@
-Meldwork Non-Commercial Source License 1.0
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
-Copyright (c) 2026 Ryder-MHumble.
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-This license applies to Meldwork and the source code, documentation, assets, and
-other materials in this repository, except where a file or third-party notice
-states a different license.
+ 1. Definitions.
-Meldwork is source-available, not open source in the OSI sense. Commercial use is
-not permitted without prior written permission from the copyright holder.
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
-1. Allowed non-commercial use
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
-You may download, inspect, run, copy, and modify Meldwork for personal learning,
-research, evaluation, security review, and other non-commercial purposes.
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
-You may share unmodified or modified copies for non-commercial purposes if you:
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
-- keep this license and all copyright, attribution, and trademark notices;
-- clearly mark your changes when you distribute a modified copy;
-- do not remove or obscure third-party notices; and
-- do not imply that the copyright holder endorses your copy or service.
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
-2. Commercial use requires permission
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
-You may not use Meldwork for a commercial purpose without a separate written
-license or other written permission from the copyright holder.
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
-Commercial purpose includes, without limitation:
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
-- selling, renting, sublicensing, or otherwise monetizing Meldwork or a modified
- version;
-- offering Meldwork or a modified version as a hosted, managed, consulting,
- outsourcing, integration, or support service;
-- embedding Meldwork code, UI, workflow, or packaged binaries in a proprietary,
- paid, or revenue-generating product;
-- using Meldwork to provide paid deliverables, paid automation, paid Agent
- operations, or other business services to a customer; and
-- use by or for a for-profit company, commercial product team, or revenue-driven
- organization as part of its business operations.
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
-If you want to use Meldwork commercially, contact the repository owner and obtain
-written permission before using, distributing, hosting, reselling, or integrating
-the product.
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
-3. No trademark license
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
-This license does not grant rights to use the Meldwork name, logo, product
-identity, screenshots, or other brand assets except as needed to describe an
-unaltered copy of the software in a truthful, non-confusing way.
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
-4. Third-party materials
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
-Third-party software, Agent products, models, names, logos, dependencies, and
-other materials remain subject to their own licenses and terms. This license does
-not give you rights that the copyright holder does not own.
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
-5. Contributions
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
-Unless a separate written agreement says otherwise, contributions submitted to
-this repository may be used under this license. The copyright holder may require
-a separate contributor agreement before accepting contributions intended for a
-commercially licensed edition.
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
-6. Termination
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
-If you violate this license, your rights under it terminate automatically. The
-copyright holder may reinstate rights in writing after the violation is cured.
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
-7. No warranty
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
-Meldwork is provided "as is", without warranties or conditions of any kind,
-including implied warranties of merchantability, fitness for a particular
-purpose, title, and non-infringement. You are responsible for evaluating whether
-the software is suitable and lawful for your use.
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
-8. Limitation of liability
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
-To the maximum extent permitted by law, the copyright holder and contributors
-will not be liable for any damages arising from or related to the software or
-this license, including direct, indirect, incidental, consequential, special,
-exemplary, punitive, or business-interruption damages.
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/NOTICE b/NOTICE
index a9f8a94..3d080db 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,9 +1,7 @@
Meldwork
Copyright 2026 Ryder-MHumble and Meldwork contributors.
-Meldwork is licensed under the Meldwork Non-Commercial Source License. See
-LICENSE. Commercial use requires separate written permission from the copyright
-holder.
+Meldwork is licensed under the Apache License 2.0. See LICENSE.
Third-party Agent names, product names, and logos remain the property of their
respective owners. They are used only to identify compatible integrations and
diff --git a/README.md b/README.md
index d50a0ae..d2c9e6f 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,15 @@
-
+
-
-
-
+

-
- English · 简体中文
-
+[Architecture](architecture.md) · [Agent Connector Contract](docs/agent-connector-sdk.md) · [Contributing](CONTRIBUTING.md) · [Code of Conduct](CODE_OF_CONDUCT.md)
+
+[English](README.md) | [简体中文](README.zh-CN.md)
+
+[](LICENSE)
+[](https://github.com/Ryder-MHumble/Meldwork/releases)
+
+
# Meldwork builds the organization layer for AI agents.
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 82aa4a9..d2d5318 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -1,18 +1,15 @@
-
+
-
-
-
+

-
- English · 简体中文
-
+[Architecture](architecture.md) · [Agent Connector Contract](docs/agent-connector-sdk.md) · [Contributing](CONTRIBUTING.md) · [Code of Conduct](CODE_OF_CONDUCT.md)
+
+[English](README.md) | [简体中文](README.zh-CN.md)
+
+[](LICENSE)
+[](https://github.com/Ryder-MHumble/Meldwork/releases)
+
+
# Meldwork 为 AI Agent 构建组织层
diff --git a/desktop/package-lock.json b/desktop/package-lock.json
index f4207ed..0e6af70 100644
--- a/desktop/package-lock.json
+++ b/desktop/package-lock.json
@@ -7,7 +7,7 @@
"": {
"name": "meldwork-desktop",
"version": "0.1.3",
- "license": "SEE LICENSE IN LICENSE",
+ "license": "Apache-2.0",
"dependencies": {
"@agentclientprotocol/sdk": "0.23.0",
"@streamparser/json": "0.0.22",
diff --git a/desktop/package.json b/desktop/package.json
index a4b9132..318cdd6 100644
--- a/desktop/package.json
+++ b/desktop/package.json
@@ -4,7 +4,7 @@
"private": true,
"description": "A local-first desktop workspace for multi-Agent collaboration",
"author": "Ryder-MHumble",
- "license": "SEE LICENSE IN LICENSE",
+ "license": "Apache-2.0",
"engines": {
"node": ">=22.12.0"
},
diff --git a/desktop/samples/local-echo.connector.json b/desktop/samples/local-echo.connector.json
index 0b1606c..eaacaa4 100644
--- a/desktop/samples/local-echo.connector.json
+++ b/desktop/samples/local-echo.connector.json
@@ -1 +1 @@
-{"manifest":{"connectorId":"external.local-echo-sample","connectorVersion":"1.0.0","credentials":{"mode":"none","slots":[]},"description":"Local SDK sample that returns text without delegating to a built-in Agent.","domains":["general"],"eventProtocolVersion":1,"eventTypes":["Completed","Failed","Cancelled"],"inputTypes":["text"],"invocation":{"recipeId":"sdk.local-echo.v1"},"kind":"agent","label":"Local Echo Connector Sample","license":"LicenseRef-Meldwork-NC","manifestId":"connector-manifest-6ce79bf9cbe282e9f537a1b45d742cc507e21ade5ea2859dff9e007bd4f850db","outboundDestinations":[],"permissionModes":["read-only"],"recordType":"agent-connector-manifest","schemaVersion":1,"session":{"cancel":true,"checkpoint":false,"resume":true,"supported":true},"transport":{"protocol":"json","type":"cli"},"upstream":{"id":"meldwork-sdk","maxVersion":"1.0.0","minVersion":"1.0.0"},"usage":{"costMicros":false,"elapsedMs":false,"inputTokens":false,"outboundBytes":false,"outputTokens":false,"toolCalls":false}},"packageId":"connector-package-f4908cee8fa7acf5d17a403b39bd58cb8d4a1dfec3fbfb43c62c5727ee793cd4","provider":{"config":{},"id":"sdk.local-echo.v1"},"publisher":{"id":"meldwork","name":"Meldwork"},"recordType":"agent-connector-package","schemaVersion":1}
+{"manifest":{"connectorId":"external.local-echo-sample","connectorVersion":"1.0.0","credentials":{"mode":"none","slots":[]},"description":"Local SDK sample that returns text without delegating to a built-in Agent.","domains":["general"],"eventProtocolVersion":1,"eventTypes":["Completed","Failed","Cancelled"],"inputTypes":["text"],"invocation":{"recipeId":"sdk.local-echo.v1"},"kind":"agent","label":"Local Echo Connector Sample","license":"Apache-2.0","manifestId":"connector-manifest-6ce79bf9cbe282e9f537a1b45d742cc507e21ade5ea2859dff9e007bd4f850db","outboundDestinations":[],"permissionModes":["read-only"],"recordType":"agent-connector-manifest","schemaVersion":1,"session":{"cancel":true,"checkpoint":false,"resume":true,"supported":true},"transport":{"protocol":"json","type":"cli"},"upstream":{"id":"meldwork-sdk","maxVersion":"1.0.0","minVersion":"1.0.0"},"usage":{"costMicros":false,"elapsedMs":false,"inputTokens":false,"outboundBytes":false,"outputTokens":false,"toolCalls":false}},"packageId":"connector-package-f4908cee8fa7acf5d17a403b39bd58cb8d4a1dfec3fbfb43c62c5727ee793cd4","provider":{"config":{},"id":"sdk.local-echo.v1"},"publisher":{"id":"meldwork","name":"Meldwork"},"recordType":"agent-connector-package","schemaVersion":1}
diff --git a/desktop/src/agents/cli/cli-adapters.cjs b/desktop/src/agents/cli/cli-adapters.cjs
index f7a2c5e..f2b091f 100644
--- a/desktop/src/agents/cli/cli-adapters.cjs
+++ b/desktop/src/agents/cli/cli-adapters.cjs
@@ -15,6 +15,7 @@ const {
parseMimoOutput,
parseOpenCodeOutput,
parseOpenCodeReviewOutput,
+ parsePiOutput,
parseWorkBuddyOutput,
readHermesFinalResponse,
readHermesMessageWatermark,
@@ -463,6 +464,7 @@ module.exports = {
parseMimoOutput,
parseOpenCodeOutput,
parseOpenCodeReviewOutput,
+ parsePiOutput,
parseWorkBuddyOutput,
prepareCommand,
readHermesFinalResponse,
diff --git a/desktop/src/agents/cli/cli-event-profiles.cjs b/desktop/src/agents/cli/cli-event-profiles.cjs
index c708d24..75e9432 100644
--- a/desktop/src/agents/cli/cli-event-profiles.cjs
+++ b/desktop/src/agents/cli/cli-event-profiles.cjs
@@ -14,6 +14,7 @@ const {
parseMimoOutput,
parseOpenCodeOutput,
parseOpenCodeReviewOutput,
+ parsePiOutput,
parseWorkBuddyOutput,
readHermesFinalResponse,
readHermesMessageWatermark,
@@ -31,6 +32,7 @@ const {
kimiStreamJsonRuntimeEvents,
mimoJsonRuntimeEvents,
openCodeJsonRuntimeEvents,
+ piJsonRuntimeEvents,
} = require('./cli-runtime-event-mappers.cjs')
function createStatelessRuntimeState() {
@@ -75,7 +77,7 @@ const FINAL_OUTPUT_PARSERS = Object.freeze({
codex: parseCodexOutput,
openclaw: stdout => ({ text: normalizeOpenClawOutput(stdout), sessionRef: '' }),
workbuddy: parseWorkBuddyOutput,
- pi: parseOpenCodeOutput,
+ pi: parsePiOutput,
kimi: parseKimiOutput,
mimo: parseMimoOutput,
claude: parseClaudeQwenOutput,
@@ -365,7 +367,7 @@ const PI_JSON_EVENTS_PROFILE = profile({
},
createDecoder: createJsonLineParser,
createState: createStatelessRuntimeState,
- mapEvent: openCodeJsonRuntimeEvents,
+ mapEvent: piJsonRuntimeEvents,
createFinalOutputAccumulator: createFinalOutputAccumulator('pi'),
})
@@ -467,7 +469,6 @@ const TRANSPORT_EVENT_PROFILES = Object.freeze({
openclaw: Object.freeze({ legacy: OPENCLAW_TERMINAL_DOCUMENT_PROFILE }),
kimi: Object.freeze({ 'stream-json': KIMI_STREAM_JSON_PROFILE }),
mimo: Object.freeze({ acp: ACP_JSONRPC_PROFILE }),
- pi: Object.freeze({ acp: ACP_JSONRPC_PROFILE }),
opencode: Object.freeze({ acp: ACP_JSONRPC_PROFILE }),
workbuddy: Object.freeze({ 'stream-json': ANTHROPIC_STREAM_JSON_PROFILE }),
})
diff --git a/desktop/src/agents/cli/cli-invocations.cjs b/desktop/src/agents/cli/cli-invocations.cjs
index faa95cb..4bdfca1 100644
--- a/desktop/src/agents/cli/cli-invocations.cjs
+++ b/desktop/src/agents/cli/cli-invocations.cjs
@@ -140,24 +140,20 @@ function invocation(kind, executable, workdir, sessionRef = '', options = {}) {
}
}
if (kind === 'pi') {
- if (options.invocationTransport !== 'json') {
- return {
- command: executable,
- args: ['acp', '--pure', '--cwd', workdir],
- acpMode: options.sandbox === 'workspace-write' ? 'build' : 'plan',
- eventTransport: 'acp',
- fallbackTransport: 'json',
- fallbackSessionPolicy: 'preserve',
- }
- }
return {
command: executable,
args: [
- 'run', '--format', 'json',
- '--agent', options.sandbox === 'workspace-write' ? 'build' : 'plan',
- ...(sessionRef ? ['--session', sessionRef] : []),
+ '--mode', 'json',
+ ...(options.sandbox === 'workspace-write'
+ ? []
+ : ['--tools', 'read,grep,find,ls']),
+ ...(options.provider?.id ? ['--provider', options.provider.id] : []),
+ ...(options.provider?.model ? ['--model', options.provider.model] : []),
+ ...(sessionRef ? ['--session-id', sessionRef] : []),
+ '-p',
],
promptArg: true,
+ eventTransport: 'pi-json-events',
}
}
if (kind === 'kimi') {
diff --git a/desktop/src/agents/cli/cli-output-parsers.cjs b/desktop/src/agents/cli/cli-output-parsers.cjs
index 1f633ac..c7b64b3 100644
--- a/desktop/src/agents/cli/cli-output-parsers.cjs
+++ b/desktop/src/agents/cli/cli-output-parsers.cjs
@@ -154,7 +154,29 @@ function ingestStructuredEvent(state, event) {
return
}
- if (['pi', 'mimo', 'opencode'].includes(state.kind)) {
+ if (state.kind === 'pi') {
+ if (event.type === 'session' && typeof event.id === 'string') state.sessionRef = event.id
+ if (event.type === 'message_update'
+ && event.assistantMessageEvent?.type === 'text_delta'
+ && typeof event.assistantMessageEvent.delta === 'string') {
+ appendStructuredText(state, event.assistantMessageEvent.delta, '')
+ }
+ if (event.type === 'message_end'
+ && event.message?.role === 'assistant'
+ && Array.isArray(event.message.content)
+ && !state.text.trim()) {
+ appendStructuredText(state, piMessageContentText(event.message.content), '')
+ }
+ if (event.type === 'agent_settled') state.outcome = 'completed'
+ if (event.type === 'turn_end') {
+ const reason = String(event.message?.stopReason || event.message?.rawStopReason || '')
+ state.outcome = reason === 'stop' ? 'completed' : (reason ? stepFinishOutcome(reason) : 'completed')
+ if (state.outcome === 'failed') markStructuredFailure(state)
+ }
+ return
+ }
+
+ if (['mimo', 'opencode'].includes(state.kind)) {
if (typeof event.sessionID === 'string') state.sessionRef = event.sessionID
if (event.type === 'text' && event.part?.type === 'text'
&& typeof event.part.text === 'string') {
@@ -437,6 +459,45 @@ function parseMimoOutput(stdout) {
return { text: texts.join('\n').trim(), sessionRef, error: errors.join('\n').trim() }
}
+function piMessageContentText(content) {
+ return (Array.isArray(content) ? content : [])
+ .map(part => (part?.type === 'text' && typeof part.text === 'string' ? part.text : ''))
+ .filter(Boolean)
+ .join('\n')
+}
+
+function parsePiOutput(stdout) {
+ const raw = String(stdout || '').trim()
+ if (!raw) return { text: '', sessionRef: '' }
+ let sessionRef = ''
+ const deltas = []
+ let finalText = ''
+ let parsedLine = false
+ for (const line of raw.split(/\r?\n/)) {
+ if (!line.trim()) continue
+ try {
+ const event = JSON.parse(line)
+ parsedLine = true
+ if (event?.type === 'session' && typeof event.id === 'string') sessionRef = event.id
+ if (event?.type === 'message_update'
+ && event.assistantMessageEvent?.type === 'text_delta'
+ && typeof event.assistantMessageEvent.delta === 'string') {
+ deltas.push(event.assistantMessageEvent.delta)
+ }
+ if (event?.type === 'message_end'
+ && event.message?.role === 'assistant'
+ && Array.isArray(event.message.content)) {
+ const text = piMessageContentText(event.message.content)
+ if (text) finalText = text
+ }
+ } catch { /* fall back to raw output when the CLI did not emit JSONL */ }
+ }
+ return {
+ text: finalText.trim() || deltas.join('').trim() || (parsedLine ? '' : raw),
+ sessionRef,
+ }
+}
+
function normalizeOpenClawOutput(stdout) {
const raw = String(stdout || '').trim()
if (!raw) return ''
@@ -718,7 +779,15 @@ function classifyCliOutcome(kind, stdout) {
: 'partial',
}
}
- if (['pi', 'mimo', 'opencode'].includes(kind)) {
+ if (kind === 'pi') {
+ const turn = events.findLast(event => event?.type === 'turn_end')
+ if (turn) {
+ const reason = String(turn.message?.stopReason || turn.message?.rawStopReason || '')
+ return { outcome: reason === 'stop' || !reason ? 'completed' : stepFinishOutcome(reason) }
+ }
+ return { outcome: events.some(event => event?.type === 'agent_settled') ? 'completed' : 'partial' }
+ }
+ if (['mimo', 'opencode'].includes(kind)) {
const finish = events.findLast(event => (
event?.type === 'step_finish' || event?.part?.type === 'step-finish'
))
@@ -832,6 +901,7 @@ module.exports = {
parseJsonOutputEvents,
parseKimiOutput,
parseMimoOutput,
+ parsePiOutput,
parseOpenCodeOutput,
parseOpenCodeReviewOutput,
parseWorkBuddyOutput,
diff --git a/desktop/src/agents/cli/cli-runtime-event-mappers.cjs b/desktop/src/agents/cli/cli-runtime-event-mappers.cjs
index c7f5a42..ca69c55 100644
--- a/desktop/src/agents/cli/cli-runtime-event-mappers.cjs
+++ b/desktop/src/agents/cli/cli-runtime-event-mappers.cjs
@@ -345,6 +345,20 @@ function openCodeJsonRuntimeEvents(event) {
return []
}
+function piJsonRuntimeEvents(event) {
+ if (!event || typeof event !== 'object') return []
+ if (event.type === 'message_update'
+ && event.assistantMessageEvent?.type === 'text_delta'
+ && typeof event.assistantMessageEvent.delta === 'string') {
+ return [{
+ type: 'answer_delta',
+ status: 'running',
+ delta: event.assistantMessageEvent.delta,
+ }]
+ }
+ return []
+}
+
function finalOnlyRuntimeEvents() {
return []
}
@@ -432,4 +446,5 @@ module.exports = {
kimiStreamJsonRuntimeEvents,
mimoJsonRuntimeEvents,
openCodeJsonRuntimeEvents,
+ piJsonRuntimeEvents,
}
diff --git a/desktop/src/agents/cli/cli-runtime-events.cjs b/desktop/src/agents/cli/cli-runtime-events.cjs
index 8713f82..470544d 100644
--- a/desktop/src/agents/cli/cli-runtime-events.cjs
+++ b/desktop/src/agents/cli/cli-runtime-events.cjs
@@ -13,6 +13,7 @@ const {
parseMimoOutput,
parseOpenCodeOutput,
parseOpenCodeReviewOutput,
+ parsePiOutput,
parseWorkBuddyOutput,
readHermesFinalResponse,
readHermesMessageWatermark,
@@ -53,6 +54,7 @@ module.exports = {
parseMimoOutput,
parseOpenCodeOutput,
parseOpenCodeReviewOutput,
+ parsePiOutput,
parseWorkBuddyOutput,
readHermesFinalResponse,
readHermesMessageWatermark,
diff --git a/desktop/src/agents/connectors/agent-connector-local.cjs b/desktop/src/agents/connectors/agent-connector-local.cjs
index 3851505..8ecc6ff 100644
--- a/desktop/src/agents/connectors/agent-connector-local.cjs
+++ b/desktop/src/agents/connectors/agent-connector-local.cjs
@@ -48,7 +48,7 @@ const SAMPLE_AGENT_CONNECTOR_MANIFEST = createAgentConnectorManifest({
},
outboundDestinations: [],
credentials: { mode: 'none', slots: [] },
- license: 'LicenseRef-Meldwork-NC',
+ license: 'Apache-2.0',
})
const SAMPLE_CREDENTIAL_AGENT_CONNECTOR_MANIFEST = createAgentConnectorManifest({
connectorId: 'external.codex-provider-sample',
@@ -81,7 +81,7 @@ const SAMPLE_CREDENTIAL_AGENT_CONNECTOR_MANIFEST = createAgentConnectorManifest(
mode: 'credential-ref',
slots: [{ slotId: 'openai-api-key', type: 'api-key', required: true }],
},
- license: 'LicenseRef-Meldwork-NC',
+ license: 'Apache-2.0',
})
const APPROVED_AGENT_CONNECTOR_MANIFESTS = Object.freeze([
SAMPLE_AGENT_CONNECTOR_MANIFEST,
@@ -116,7 +116,7 @@ const SAMPLE_LOCAL_ECHO_AGENT_CONNECTOR_MANIFEST = createAgentConnectorManifest(
},
outboundDestinations: [],
credentials: { mode: 'none', slots: [] },
- license: 'LicenseRef-Meldwork-NC',
+ license: 'Apache-2.0',
})
const SAMPLE_LOCAL_ECHO_AGENT_CONNECTOR_PACKAGE = createAgentConnectorPackage({
publisher: { id: 'meldwork', name: 'Meldwork' },
diff --git a/desktop/test/agents/cli/cli-adapters-agent-protocols.test.cjs b/desktop/test/agents/cli/cli-adapters-agent-protocols.test.cjs
index 00dd8c5..372bdb5 100644
--- a/desktop/test/agents/cli/cli-adapters-agent-protocols.test.cjs
+++ b/desktop/test/agents/cli/cli-adapters-agent-protocols.test.cjs
@@ -19,6 +19,7 @@ const {
parseMimoOutput,
parseOpenCodeOutput,
parseOpenCodeReviewOutput,
+ parsePiOutput,
parseWorkBuddyOutput,
prepareCommand,
readHermesFinalResponse,
@@ -1062,6 +1063,67 @@ test('MiMo JSON output returns final text and session id', () => {
})
})
+test('Pi uses the current non-interactive JSON mode and resumes by session id', () => {
+ const readOnly = invocation('pi', '/tmp/pi', '/tmp/work', 'pi-session', {
+ provider: { id: 'zgci', model: 'glm' },
+ })
+ assert.deepEqual(readOnly.args, [
+ '--mode', 'json',
+ '--tools', 'read,grep,find,ls',
+ '--provider', 'zgci',
+ '--model', 'glm',
+ '--session-id', 'pi-session',
+ '-p',
+ ])
+ assert.equal(readOnly.promptArg, true)
+ assert.equal(readOnly.eventTransport, 'pi-json-events')
+
+ const writable = invocation('pi', '/tmp/pi', '/tmp/work', '', {
+ sandbox: 'workspace-write',
+ })
+ assert.deepEqual(writable.args, ['--mode', 'json', '-p'])
+})
+
+test('Pi JSON output returns final text and session id from current CLI events', () => {
+ const raw = [
+ JSON.stringify({
+ type: 'session',
+ id: '01a03794-ae3f-72ea-9efd-cd2cd16d88a1',
+ cwd: '/tmp/work',
+ }),
+ JSON.stringify({
+ type: 'message_update',
+ assistantMessageEvent: { type: 'text_delta', contentIndex: 0, delta: 'Pi ' },
+ }),
+ JSON.stringify({
+ type: 'message_update',
+ assistantMessageEvent: { type: 'text_delta', contentIndex: 0, delta: 'reply' },
+ }),
+ JSON.stringify({
+ type: 'message_end',
+ message: {
+ role: 'assistant',
+ content: [{ type: 'text', text: 'Pi reply' }],
+ stopReason: 'stop',
+ },
+ }),
+ JSON.stringify({
+ type: 'turn_end',
+ message: { role: 'assistant', stopReason: 'stop' },
+ }),
+ JSON.stringify({ type: 'agent_settled' }),
+ ].join('\n')
+ assert.deepEqual(parsePiOutput(raw), {
+ text: 'Pi reply',
+ sessionRef: '01a03794-ae3f-72ea-9efd-cd2cd16d88a1',
+ })
+ assert.deepEqual(profileOutput('pi', raw), {
+ text: 'Pi reply',
+ sessionRef: '01a03794-ae3f-72ea-9efd-cd2cd16d88a1',
+ outcome: 'completed',
+ })
+})
+
test('Claude and Qwen stream JSON output returns the final reply and session id', () => {
for (const kind of ['claude', 'qwen']) {
const raw = [
@@ -1598,12 +1660,19 @@ test('every supported built-in Agent produces an explicit completion outcome', (
].join('\n'),
pi: [
JSON.stringify({
- type: 'text', sessionID: 'pi-session',
- part: { type: 'text', text: 'Pi Agent reply' },
+ type: 'session', id: 'pi-session', version: 3,
}),
JSON.stringify({
- type: 'step_finish', sessionID: 'pi-session',
- part: { type: 'step-finish', reason: 'stop' },
+ type: 'message_update',
+ assistantMessageEvent: { type: 'text_delta', contentIndex: 0, delta: 'Pi Agent reply' },
+ }),
+ JSON.stringify({
+ type: 'message_end',
+ message: { role: 'assistant', content: [{ type: 'text', text: 'Pi Agent reply' }] },
+ }),
+ JSON.stringify({
+ type: 'turn_end',
+ message: { role: 'assistant', stopReason: 'stop', rawStopReason: 'stop' },
}),
].join('\n'),
claude: JSON.stringify({
diff --git a/desktop/test/agents/cli/cli-adapters.test.cjs b/desktop/test/agents/cli/cli-adapters.test.cjs
index f2141a9..78647b2 100644
--- a/desktop/test/agents/cli/cli-adapters.test.cjs
+++ b/desktop/test/agents/cli/cli-adapters.test.cjs
@@ -15,6 +15,7 @@ const {
parseKimiOutput,
parseMimoOutput,
parseOpenCodeOutput,
+ parsePiOutput,
parseWorkBuddyOutput,
prepareCommand,
readHermesFinalResponse,
@@ -1521,6 +1522,23 @@ test('Hermes receives selected Skill snapshots through the prompt without native
assert.deepEqual(acpSpec.args, ['acp'])
})
+test('Pi receives provider settings through supported top-level JSON flags', () => {
+ const spec = invocation('pi', '/tmp/pi', '/tmp', 'pi-session-123', {
+ provider: { id: 'zgci', model: 'glm' },
+ })
+ assert.deepEqual(spec.args, [
+ '--mode', 'json',
+ '--tools', 'read,grep,find,ls',
+ '--provider', 'zgci',
+ '--model', 'glm',
+ '--session-id', 'pi-session-123',
+ '-p',
+ ])
+ assert.equal(spec.promptArg, true)
+ assert.equal(spec.eventTransport, 'pi-json-events')
+ assert.equal(typeof parsePiOutput, 'function')
+})
+
test('Hermes forwards one image and rejects additional images instead of dropping them', () => {
const first = path.resolve('diagram-one.png')
const second = path.resolve('diagram-two.png')
diff --git a/desktop/test/agents/cli/cli-event-profiles.test.cjs b/desktop/test/agents/cli/cli-event-profiles.test.cjs
index 810cb81..a9f8107 100644
--- a/desktop/test/agents/cli/cli-event-profiles.test.cjs
+++ b/desktop/test/agents/cli/cli-event-profiles.test.cjs
@@ -133,11 +133,7 @@ test('every built-in invocation transport resolves its reusable profile and capa
kind: 'mimo', options: { invocationTransport: 'json' },
profileId: 'mimo-json-events-v1', limited: true,
},
- { kind: 'pi', profileId: 'acp-jsonrpc-v1', limited: false },
- {
- kind: 'pi', options: { invocationTransport: 'json' },
- profileId: 'pi-json-events-v1', limited: true,
- },
+ { kind: 'pi', profileId: 'pi-json-events-v1', limited: true },
{ kind: 'claude', profileId: 'anthropic-stream-json-v1', limited: false },
{ kind: 'gemini', profileId: 'gemini-stream-json-v1', limited: false },
{ kind: 'opencode', profileId: 'acp-jsonrpc-v1', limited: false },
diff --git a/desktop/test/agents/cli/cli-runtime-event-protocols.test.cjs b/desktop/test/agents/cli/cli-runtime-event-protocols.test.cjs
index fef0c46..bd8db42 100644
--- a/desktop/test/agents/cli/cli-runtime-event-protocols.test.cjs
+++ b/desktop/test/agents/cli/cli-runtime-event-protocols.test.cjs
@@ -54,9 +54,9 @@ const STREAMING_FIXTURES = [
{
kind: 'pi',
agent: 'Pi Agent',
- version: '0.1.0',
+ version: '0.84.2',
protocol: 'pi-json-events',
- evidence: 'source-derived',
+ evidence: 'captured-fixture',
file: 'pi.jsonl',
expectedTypes: ['answer_delta'],
},
diff --git a/desktop/test/fixtures/agent-output/manifest.json b/desktop/test/fixtures/agent-output/manifest.json
index f9e4fe7..6c3c325 100644
--- a/desktop/test/fixtures/agent-output/manifest.json
+++ b/desktop/test/fixtures/agent-output/manifest.json
@@ -5,7 +5,7 @@
{ "kind": "hermes", "agent": "Hermes", "version": "0.19.1", "protocol": "terminal-text", "evidence": "captured-fixture", "file": "hermes.txt" },
{ "kind": "openclaw", "transport": "legacy", "agent": "OpenClaw", "version": "2026.7.1-2", "protocol": "openclaw-terminal-document", "evidence": "captured-fixture", "file": "openclaw.json" },
{ "kind": "workbuddy", "agent": "WorkBuddy", "version": "2.115.0", "protocol": "workbuddy-terminal-result-json", "evidence": "source-derived", "file": "workbuddy.jsonl", "variants": [{ "transport": "stream-json", "agent": "WorkBuddy", "version": "2.115.0", "protocol": "anthropic-stream-json", "evidence": "source-derived", "file": "workbuddy-stream-json.jsonl" }] },
- { "kind": "pi", "agent": "Pi Agent", "version": "0.1.0", "protocol": "pi-json-events", "evidence": "source-derived", "file": "pi.jsonl" },
+ { "kind": "pi", "agent": "Pi Agent", "version": "0.84.2", "protocol": "pi-json-events", "evidence": "captured-fixture", "file": "pi.jsonl" },
{ "kind": "kimi", "transport": "stream-json", "agent": "Kimi", "version": "0.19.2", "protocol": "assistant-jsonl", "evidence": "captured-fixture", "file": "kimi.jsonl" },
{ "kind": "mimo", "agent": "MiMo", "version": "0.1.0", "protocol": "mimo-json-events", "evidence": "captured-fixture", "file": "mimo.jsonl" },
{ "kind": "claude", "agent": "Claude", "version": "2.1.165", "protocol": "anthropic-stream-json", "evidence": "captured-fixture", "file": "claude.jsonl" },
diff --git a/desktop/test/fixtures/agent-output/pi.jsonl b/desktop/test/fixtures/agent-output/pi.jsonl
index ae1c8df..0371ed1 100644
--- a/desktop/test/fixtures/agent-output/pi.jsonl
+++ b/desktop/test/fixtures/agent-output/pi.jsonl
@@ -1,2 +1,8 @@
-{"type":"text","sessionID":"pi-fixture-session","part":{"type":"text","text":"Pi Agent fixture reply"}}
-{"type":"step_finish","sessionID":"pi-fixture-session","part":{"type":"step-finish","reason":"stop"}}
+{"type":"session","version":3,"id":"pi-fixture-session","cwd":"/tmp/work"}
+{"type":"agent_start"}
+{"type":"turn_start"}
+{"type":"message_update","assistantMessageEvent":{"type":"text_delta","contentIndex":0,"delta":"Pi Agent fixture reply"}}
+{"type":"message_end","message":{"role":"assistant","content":[{"type":"text","text":"Pi Agent fixture reply"}],"stopReason":"stop","rawStopReason":"stop"}}
+{"type":"turn_end","message":{"role":"assistant","content":[{"type":"text","text":"Pi Agent fixture reply"}],"stopReason":"stop","rawStopReason":"stop"}}
+{"type":"agent_end"}
+{"type":"agent_settled"}
diff --git a/desktop/test/workspace/local-workspace-native-streaming.test.cjs b/desktop/test/workspace/local-workspace-native-streaming.test.cjs
index 11c6ea7..c21d292 100644
--- a/desktop/test/workspace/local-workspace-native-streaming.test.cjs
+++ b/desktop/test/workspace/local-workspace-native-streaming.test.cjs
@@ -39,6 +39,61 @@ test('direct Hermes conversations reuse one persistent ACP runtime key across me
assert.equal(calls[1].runOptions.acpPersistenceKey, calls[0].runOptions.acpPersistenceKey)
})
+test('direct Pi conversations reuse the native JSON session across messages', async (t) => {
+ const { directory, calls, options } = fixture()
+ t.after(() => fs.rmSync(directory, { recursive: true, force: true }))
+ options.detectAgents = async () => [{
+ kind: 'pi', name: 'Pi Agent', executable: '/tmp/pi', version: '0.84.2',
+ }]
+ options.runAgent = async (agent, prompt, workdir, runOptions) => {
+ calls.push({ agent, prompt, workdir, runOptions })
+ const sessionRef = runOptions.sessionRef || 'pi-json-session'
+ await runOptions.onSessionRef(sessionRef)
+ return { text: `pi reply ${calls.length}`, sessionRef, outcome: 'completed' }
+ }
+ const workspace = new LocalWorkspace(options)
+ await workspace.refreshAgents()
+ const group = workspace.createGroup({
+ name: 'Pi direct', agentKinds: ['pi'], workdir: directory,
+ conversationType: 'direct', directAgentKind: 'pi',
+ })
+
+ await workspace.sendMessage({ groupId: group.id, text: 'first' })
+ await workspace.sendMessage({ groupId: group.id, text: 'second' })
+
+ assert.deepEqual(calls.map(call => call.agent.kind), ['pi', 'pi'])
+ assert.deepEqual(calls.map(call => call.runOptions.sessionRef), ['', 'pi-json-session'])
+ assert.deepEqual(calls.map(call => call.runOptions.sessionTransport), ['', ''])
+})
+
+test('group conversations dispatch Pi alongside other available Agents', async (t) => {
+ const { directory, calls, options } = fixture()
+ t.after(() => fs.rmSync(directory, { recursive: true, force: true }))
+ options.detectAgents = async () => [
+ { kind: 'pi', name: 'Pi Agent', executable: '/tmp/pi', version: '0.84.2' },
+ { kind: 'codex', name: 'Codex', executable: '/tmp/codex', version: '0.144.0' },
+ ]
+ options.runAgent = async (agent, prompt, workdir, runOptions) => {
+ calls.push({ agent, prompt, workdir, runOptions })
+ const sessionRef = runOptions.sessionRef || `${agent.kind}-session`
+ await runOptions.onSessionRef(sessionRef)
+ return { text: `${agent.kind} reply`, sessionRef, outcome: 'completed' }
+ }
+ const workspace = new LocalWorkspace(options)
+ await workspace.refreshAgents()
+ const group = workspace.createGroup({
+ name: 'Pi group', agentKinds: ['pi', 'codex'], workdir: directory,
+ })
+
+ await workspace.sendMessage({
+ groupId: group.id, text: 'ask the group', targetKinds: ['pi', 'codex'],
+ })
+
+ assert.deepEqual(calls.map(call => call.agent.kind).sort(), ['codex', 'pi'])
+ const messages = workspace.snapshot().messages.filter(item => item.role === 'agent')
+ assert.deepEqual(messages.map(item => item.agentKind).sort(), ['codex', 'pi'])
+})
+
test('Workspace stream bridge redacts split credentials and paths while preserving tools', async (t) => {
const { directory, options } = fixture()
t.after(() => fs.rmSync(directory, { recursive: true, force: true }))
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index b5802d0..0f178b7 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -7,7 +7,7 @@
"": {
"name": "meldwork-frontend",
"version": "0.1.3",
- "license": "SEE LICENSE IN LICENSE",
+ "license": "Apache-2.0",
"dependencies": {
"@vicons/ionicons5": "^0.12.0",
"dompurify": "^3.4.13",
diff --git a/frontend/package.json b/frontend/package.json
index 393861a..f1b6085 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -4,7 +4,7 @@
"private": true,
"description": "Meldwork local-first multi-Agent collaboration workspace",
"author": "Ryder-MHumble",
- "license": "SEE LICENSE IN LICENSE",
+ "license": "Apache-2.0",
"engines": {
"node": ">=22.12.0"
},