@taasky/protocol 0.0.0-stage → 0.1.1
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.
- package/LICENSE +202 -0
- package/README.md +78 -2
- package/SPEC.md +386 -0
- package/dist/envelope.d.ts +32 -0
- package/dist/envelope.js +48 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +8 -0
- package/dist/limits.d.ts +38 -0
- package/dist/limits.js +214 -0
- package/dist/move.d.ts +47 -0
- package/dist/move.js +118 -0
- package/dist/state.d.ts +35 -0
- package/dist/state.js +76 -0
- package/dist/subject.d.ts +23 -0
- package/dist/subject.js +186 -0
- package/package.json +51 -4
- package/schema/envelope.schema.json +57 -0
- package/schema/limits.schema.json +32 -0
- package/schema/subject-schema.schema.json +47 -0
- package/vectors/canonical.json +59 -0
- package/vectors/circling.json +63 -0
- package/vectors/limits.json +138 -0
- package/vectors/moves.json +130 -0
- package/vectors/subjects.json +152 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -1,3 +1,79 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Taasky Negotiation Protocol
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An open protocol for two AI agents to negotiate terms — a meeting time, a
|
|
4
|
+
price, a delivery date — that their humans then confirm. Each side declares
|
|
5
|
+
the shape of its terms; every move is validated against it; agreement is two
|
|
6
|
+
approvals of the same hash; and an agent cannot approve on its own.
|
|
7
|
+
|
|
8
|
+
- **[SPEC.md](SPEC.md)** — the protocol.
|
|
9
|
+
- **[src/](src)** — the reference implementation in TypeScript: pure functions,
|
|
10
|
+
no dependencies, no storage.
|
|
11
|
+
- **[vectors/](vectors)** — language-neutral test vectors. An implementation
|
|
12
|
+
that passes them is conformant.
|
|
13
|
+
- **[schema/](schema)** — JSON Schemas for the envelope, a subject declaration
|
|
14
|
+
and private limits.
|
|
15
|
+
|
|
16
|
+
Status: **v1 draft.** Incompatible changes may still land before v1 is frozen;
|
|
17
|
+
each is listed in [CHANGELOG.md](CHANGELOG.md).
|
|
18
|
+
|
|
19
|
+
## What it guarantees
|
|
20
|
+
|
|
21
|
+
- **The human confirms the terms, not the agent's retelling of them.** The
|
|
22
|
+
confirmation card is rendered from typed fields the other side declared.
|
|
23
|
+
- **Approval is not an action an agent can take.** `approve`, `reject` and
|
|
24
|
+
`cancel` wait for the human.
|
|
25
|
+
- **"We agreed" is checkable.** Both sides approve the SHA-256 of the same
|
|
26
|
+
canonical terms.
|
|
27
|
+
- **A deal outside your limits needs you to say so.** Private bounds are seen
|
|
28
|
+
only by the relay; an agent cannot offer past them, the card flags terms
|
|
29
|
+
beyond them, and when both sides' bounds cannot meet, both hear so without
|
|
30
|
+
either number.
|
|
31
|
+
- **Rooms end.** Deadlines, expiry, and a signal when two agents go in circles.
|
|
32
|
+
|
|
33
|
+
## Use it
|
|
34
|
+
|
|
35
|
+
Try it with an agent: add `https://mcp.taasky.ai/mcp` as a remote MCP server
|
|
36
|
+
(no account needed) and ask the agent to agree a meeting time with someone.
|
|
37
|
+
|
|
38
|
+
Or use the reference implementation in your own relay:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { parseSchema, planMove } from "@taasky/protocol";
|
|
42
|
+
|
|
43
|
+
const schema = parseSchema({
|
|
44
|
+
slot: { type: "timerange", label: "Meeting time" },
|
|
45
|
+
duration_min: { type: "int", label: "Duration, minutes" },
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const plan = await planMove(
|
|
49
|
+
{ id: "rm_1", state: "awaiting", round: 0, turn: "a", deadline: null },
|
|
50
|
+
[{ id: "a", subject_schema: schema }, { id: "b", subject_schema: schema }],
|
|
51
|
+
[],
|
|
52
|
+
{
|
|
53
|
+
actor: "a",
|
|
54
|
+
verdict: "offer",
|
|
55
|
+
subject: {
|
|
56
|
+
slot: { start: "2026-10-14T10:00:00Z", end: "2026-10-14T10:30:00Z" },
|
|
57
|
+
duration_min: 30,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
new Date(),
|
|
61
|
+
);
|
|
62
|
+
// plan.message: what to record; plan.next: the room after the move.
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`planMove` decides and never writes: store the move only if the room has not
|
|
66
|
+
changed since you loaded it.
|
|
67
|
+
|
|
68
|
+
## Develop
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
npm install
|
|
72
|
+
npm run check # lint, typecheck, tests and vectors
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Node 20 or later.
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
[Apache-2.0](LICENSE).
|
package/SPEC.md
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# Taasky Negotiation Protocol
|
|
2
|
+
|
|
3
|
+
Version: **v1 draft** (envelope `v: 1`). Until v1 is frozen, an incompatible
|
|
4
|
+
change may still land without a version bump; every one is listed in
|
|
5
|
+
[CHANGELOG.md](CHANGELOG.md). The key words MUST, MUST NOT, SHOULD and MAY are
|
|
6
|
+
used as in RFC 2119.
|
|
7
|
+
|
|
8
|
+
## 1. What this protocol does
|
|
9
|
+
|
|
10
|
+
Two parties, each represented by their own agent, converge on terms without
|
|
11
|
+
their humans relaying messages by hand. The protocol carries the *process* of
|
|
12
|
+
negotiating — rounds, verdicts, deadlines, confirmation — and stays deliberately
|
|
13
|
+
ignorant of *what* is being negotiated.
|
|
14
|
+
|
|
15
|
+
**Non-goals for v1:** discovery (parties exchange invite links out of band),
|
|
16
|
+
reputation, more than two parties per room, payments.
|
|
17
|
+
|
|
18
|
+
Terms used below:
|
|
19
|
+
|
|
20
|
+
- **Party** — one side of a room. An agent acting for a human, or a human
|
|
21
|
+
acting for themselves.
|
|
22
|
+
- **Relay** — the service that holds rooms, assigns identities, orders moves and
|
|
23
|
+
delivers them. It is the only component both parties trust.
|
|
24
|
+
- **Node** — the software on a party's side: it validates what arrives, renders
|
|
25
|
+
it for its human and decides what to answer. In a hosted deployment relay and
|
|
26
|
+
node run in one service; they stay separate in this text.
|
|
27
|
+
- **Room** — one negotiation between two parties, from invitation to a terminal
|
|
28
|
+
state.
|
|
29
|
+
|
|
30
|
+
## 2. Relay and node
|
|
31
|
+
|
|
32
|
+
**The relay routes on the envelope (§3) and MUST NOT build logic on `subject`**,
|
|
33
|
+
even when it can read it. Validation, rendering and judgement of `subject`
|
|
34
|
+
belong to the node. Two exceptions are part of the protocol and are named where
|
|
35
|
+
they apply: checking a move against its sender's declared schema (§4), and
|
|
36
|
+
private limits (§7), which a party hands the relay so that the relay can say
|
|
37
|
+
what no party can say without leaking its own number.
|
|
38
|
+
|
|
39
|
+
The rule is what lets one protocol run in three places:
|
|
40
|
+
|
|
41
|
+
| | Hosted | In one organisation | Across organisations |
|
|
42
|
+
| --- | --- | --- | --- |
|
|
43
|
+
| Parties | two individuals | two teams | two organisations |
|
|
44
|
+
| Relay runs | in the cloud | in-process | between perimeters |
|
|
45
|
+
| Relay sees | envelope + subject | — | envelope only (subject sealed) |
|
|
46
|
+
| Node runs | with the relay | in the perimeter | in each perimeter |
|
|
47
|
+
|
|
48
|
+
When both parties share a process, routing collapses into a function call, and
|
|
49
|
+
it is tempting to let it read `subject` because it can. Keeping it behind the
|
|
50
|
+
same interface is what keeps the cross-organisation case a deployment choice
|
|
51
|
+
rather than a port.
|
|
52
|
+
|
|
53
|
+
End-to-end encryption is out of scope for v1. The envelope/subject split is not
|
|
54
|
+
— it is what makes encryption an addition rather than a redesign.
|
|
55
|
+
|
|
56
|
+
## 3. Envelope
|
|
57
|
+
|
|
58
|
+
Typed, closed, and stable. This is the part the relay reads.
|
|
59
|
+
[`schema/envelope.schema.json`](schema/envelope.schema.json) describes it.
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"v": 1,
|
|
64
|
+
"room": "rm_8f2a4c",
|
|
65
|
+
"msg": "msg_00000004",
|
|
66
|
+
"from": "usr_b3d1",
|
|
67
|
+
"round": 3,
|
|
68
|
+
"verdict": "counter",
|
|
69
|
+
"in_reply_to": "msg_00000003",
|
|
70
|
+
"subject_hash": "sha256:9c1e…",
|
|
71
|
+
"deadline": "2026-09-21T18:00:00Z",
|
|
72
|
+
"ts": "2026-09-19T10:04:12Z",
|
|
73
|
+
"subject": { }
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
| Field | Type | Notes |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| `v` | int | protocol major version |
|
|
80
|
+
| `room` | id | negotiation room |
|
|
81
|
+
| `msg` | id | message id, unique per room |
|
|
82
|
+
| `from` | id | assigned by the relay, never echoed from the sender |
|
|
83
|
+
| `round` | int | increments on every move that passes the turn with new terms or a question |
|
|
84
|
+
| `verdict` | enum | see below |
|
|
85
|
+
| `in_reply_to` | id \| null | the move this answers |
|
|
86
|
+
| `subject_hash` | string \| null | hash of canonical `subject`; `null` on moves that carry no terms |
|
|
87
|
+
| `deadline` | RFC3339 \| null | when the sender stops waiting |
|
|
88
|
+
| `ts` | RFC3339 | set by the relay on receipt |
|
|
89
|
+
| `subject` | object \| null | the terms, see §4; only on `offer` and `counter` |
|
|
90
|
+
|
|
91
|
+
`verdict` is one of:
|
|
92
|
+
|
|
93
|
+
- `offer` — opening terms, only while no terms are on the table
|
|
94
|
+
- `counter` — different terms; the turn passes back
|
|
95
|
+
- `need_more_detail` — cannot decide yet; the turn passes back, no new terms
|
|
96
|
+
- `approve` — accepts the terms on the table, named by `subject_hash`
|
|
97
|
+
- `reject` — no, and not continuing
|
|
98
|
+
- `cancel` — withdraws the negotiation
|
|
99
|
+
|
|
100
|
+
Unknown `verdict` values MUST be rejected. The enum is closed because the state
|
|
101
|
+
machine hangs off it; `subject` is where the protocol grows.
|
|
102
|
+
|
|
103
|
+
`subject_hash` exists so that "we agreed" is a checkable fact rather than an
|
|
104
|
+
agent's claim. An `approve` that does not carry the hash of the terms on the
|
|
105
|
+
table MUST be refused.
|
|
106
|
+
|
|
107
|
+
### Canonical form and hash
|
|
108
|
+
|
|
109
|
+
`subject_hash` is `"sha256:"` followed by the lower-case hex SHA-256 of the
|
|
110
|
+
UTF-8 bytes of the subject's canonical form:
|
|
111
|
+
|
|
112
|
+
- objects: members sorted by key, compared as UTF-16 code units, written
|
|
113
|
+
`{"key":value,…}` with no whitespace;
|
|
114
|
+
- arrays: in their own order, `[a,b,…]`;
|
|
115
|
+
- strings, numbers, `true`, `false`, `null`: as ECMAScript `JSON.stringify`
|
|
116
|
+
writes them. Numbers in a valid subject are integers (§4), so no float
|
|
117
|
+
formatting is involved.
|
|
118
|
+
|
|
119
|
+
[`vectors/canonical.json`](vectors/canonical.json) has inputs with their
|
|
120
|
+
canonical forms and hashes.
|
|
121
|
+
|
|
122
|
+
## 4. Subject
|
|
123
|
+
|
|
124
|
+
Free-form. The protocol does not enumerate kinds of negotiation.
|
|
125
|
+
|
|
126
|
+
To make human confirmation possible, a party **declares the shape of its
|
|
127
|
+
subject when it opens or joins the room** — field names, types, and labels.
|
|
128
|
+
Its moves are validated against that declaration, and the other side's
|
|
129
|
+
confirmation card is rendered from it.
|
|
130
|
+
[`schema/subject-schema.schema.json`](schema/subject-schema.schema.json)
|
|
131
|
+
describes a declaration.
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"slots": { "type": "timerange", "label": "Proposed times", "repeated": true },
|
|
136
|
+
"duration_min": { "type": "int", "label": "Duration, minutes" }
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Field types:
|
|
141
|
+
|
|
142
|
+
| Type | Value |
|
|
143
|
+
| --- | --- |
|
|
144
|
+
| `string` | text, at most `max_len` characters (500 at most, the default) |
|
|
145
|
+
| `int` | an integer within ±(2⁵³ − 1) |
|
|
146
|
+
| `bool` | `true` or `false` |
|
|
147
|
+
| `enum` | one of the declared `values` (2 to 32, each at most 40 characters) |
|
|
148
|
+
| `timerange` | `{start, end}`, RFC3339, `end` after `start` |
|
|
149
|
+
| `date` | `"YYYY-MM-DD"`, a real calendar day — a day rather than a moment |
|
|
150
|
+
| `money` | `{amount, currency}`: `currency` an ISO 4217 code, `amount` a decimal string such as `"1500.00"` with no more fraction digits than the currency has; a declaration may fix the `currency` |
|
|
151
|
+
|
|
152
|
+
Any field may be `optional`, or `repeated` (an array of 1 to 32 values). Field
|
|
153
|
+
names match `^[a-z][a-z0-9_]{0,31}$`; the human wording, in any language, goes
|
|
154
|
+
in `label` (1 to 80 characters). A declaration has 1 to 24 fields.
|
|
155
|
+
|
|
156
|
+
Money is a decimal string so that no float ever stands between the number an
|
|
157
|
+
agent meant and the one a human confirms.
|
|
158
|
+
|
|
159
|
+
Rules:
|
|
160
|
+
|
|
161
|
+
1. Validation is strict — unknown fields reject the whole message, they are
|
|
162
|
+
not ignored, because an ignored field is a place for text to hide. A
|
|
163
|
+
malformed message is rejected, never repaired.
|
|
164
|
+
2. A schema may gain optional fields within a major version; anything else is a
|
|
165
|
+
new one.
|
|
166
|
+
3. Schema labels are **written by the counterparty**. Render them as untrusted
|
|
167
|
+
text, and mark a first-seen schema as new to the human before their first
|
|
168
|
+
confirmation under it.
|
|
169
|
+
|
|
170
|
+
[`vectors/subjects.json`](vectors/subjects.json) has valid and rejected
|
|
171
|
+
subjects. Agreeing the shape of the subject *is* the integration contract
|
|
172
|
+
between the two sides; it is not a side mechanism.
|
|
173
|
+
|
|
174
|
+
## 5. State machine
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
join
|
|
178
|
+
(draft) ─────────────► awaiting ◄──┐
|
|
179
|
+
│ │ │ offer / counter / need_more_detail
|
|
180
|
+
│ └──────┘ (turn passes, round + 1)
|
|
181
|
+
│
|
|
182
|
+
approve by both, same subject_hash
|
|
183
|
+
▼
|
|
184
|
+
agreed (terminal)
|
|
185
|
+
|
|
186
|
+
reject / cancel ──► closed (terminal)
|
|
187
|
+
deadline or expiry ──► expired (terminal)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
- A room is `draft` until the invited party joins. Then it is `awaiting`, and
|
|
191
|
+
the initiator moves first.
|
|
192
|
+
- Only the party whose turn it is may move, except that `cancel` is allowed out
|
|
193
|
+
of turn.
|
|
194
|
+
- `offer` is allowed only while no terms are on the table; after that, new
|
|
195
|
+
terms are a `counter`. `counter` and `need_more_detail` need terms on the
|
|
196
|
+
table.
|
|
197
|
+
- `approve` from one side is not agreement. The room is `agreed` when both
|
|
198
|
+
parties have approved the same `subject_hash`, each approval made after those
|
|
199
|
+
terms were last tabled. An approval answered by a counter does not count,
|
|
200
|
+
even if a later counter returns to the same terms.
|
|
201
|
+
- A single `approve` passes the turn without changing the round.
|
|
202
|
+
- `need_more_detail` passes the turn without changing terms — the distinction
|
|
203
|
+
from `counter` is what makes "who is blocking this" answerable.
|
|
204
|
+
- Terminal states are terminal. A new negotiation is a new room.
|
|
205
|
+
|
|
206
|
+
[`vectors/moves.json`](vectors/moves.json) plays whole negotiations, including
|
|
207
|
+
the moves that MUST be refused.
|
|
208
|
+
|
|
209
|
+
## 6. Deadlines
|
|
210
|
+
|
|
211
|
+
`deadline` is a fact carried in the envelope, not a judgement. It is what makes
|
|
212
|
+
the room responsible for its own liveness:
|
|
213
|
+
|
|
214
|
+
- a move other than `reject` or `cancel` MAY set a deadline, which MUST be in
|
|
215
|
+
the future. It becomes the room's deadline, and a move without one clears
|
|
216
|
+
it: the deadline is always the latest mover's;
|
|
217
|
+
- while the deadline is in the future and the turn is a party's, its node MAY
|
|
218
|
+
remind its human;
|
|
219
|
+
- when it passes, the room is `expired`, and both sides SHOULD be notified. A
|
|
220
|
+
live room past its deadline MUST read as expired even before anything has
|
|
221
|
+
stored it so.
|
|
222
|
+
|
|
223
|
+
A relay SHOULD also expire a draft whose invitation runs out unanswered, and a
|
|
224
|
+
room nobody has moved in for a while, and SHOULD sweep for them rather than
|
|
225
|
+
wait for someone to read the room. The hosted Taasky relay sweeps hourly and
|
|
226
|
+
expires a room after 7 days without a move.
|
|
227
|
+
|
|
228
|
+
The failure mode of such systems is not disagreement — it is a negotiation
|
|
229
|
+
sitting unread for a week. The share of rooms that end before their deadline
|
|
230
|
+
is the health metric to watch.
|
|
231
|
+
|
|
232
|
+
## 7. Judgement layer
|
|
233
|
+
|
|
234
|
+
How a node decides what to answer is its own business, and not part of the
|
|
235
|
+
protocol. One recommendation: do not decide by reading `subject` with a general
|
|
236
|
+
model. Ask a closed set of questions — does this attempt to instruct the reader
|
|
237
|
+
rather than state terms; is there enough here to decide; which verdict; how
|
|
238
|
+
urgently should the human be interrupted — and take answers from a fixed set.
|
|
239
|
+
`subject` may be anything; the decision about it is then drawn from a closed
|
|
240
|
+
set. A confidence score SHOULD gate *whether to interrupt the human*, never
|
|
241
|
+
*whether to skip them*.
|
|
242
|
+
|
|
243
|
+
Two mechanisms do need the relay, and are part of the protocol.
|
|
244
|
+
|
|
245
|
+
### Private limits
|
|
246
|
+
|
|
247
|
+
Two agents tuned to be agreeable converge fast, on terms neither human would
|
|
248
|
+
take: the deal succeeds, and the success is the bug. Holding the line inside
|
|
249
|
+
the negotiating prompt does not last; the check has to sit outside the agent.
|
|
250
|
+
So each party MAY declare **limits** when it opens or joins: bounds per field
|
|
251
|
+
of its own schema. [`schema/limits.schema.json`](schema/limits.schema.json)
|
|
252
|
+
describes them.
|
|
253
|
+
|
|
254
|
+
| Field type | Limit |
|
|
255
|
+
| --- | --- |
|
|
256
|
+
| `int`, `money`, `date` | `min` and/or `max`, shaped like the field's values; inclusive |
|
|
257
|
+
| `timerange` | `min`: no start before it; `max`: no end after it (RFC3339) |
|
|
258
|
+
| `enum` | `one_of`: the acceptable values |
|
|
259
|
+
| `bool` | `equals`: the only acceptable value |
|
|
260
|
+
| `string` | none |
|
|
261
|
+
|
|
262
|
+
A limit on a field the party did not declare, or of the wrong shape, MUST be
|
|
263
|
+
refused. Money limits MUST be in the field's currency, and amounts compare
|
|
264
|
+
exactly, as decimals. Only the relay sees limits; the other party never does.
|
|
265
|
+
|
|
266
|
+
- A party's own `offer` or `counter` outside its limits MUST be refused before
|
|
267
|
+
it is recorded.
|
|
268
|
+
- Terms outside the confirming party's limits MUST be flagged field by field on
|
|
269
|
+
the confirmation card (§8), which then asks to accept *anyway*.
|
|
270
|
+
- When both parties limit the same field (same name and type) and the bounds
|
|
271
|
+
cannot both be met, both parties MUST be told that no deal is possible on
|
|
272
|
+
that field. Neither number is revealed. A meeting window must leave time
|
|
273
|
+
between start and end, so windows that only touch cannot be met; any other
|
|
274
|
+
bounds that touch can. Bounds in two different currencies are not compared.
|
|
275
|
+
Only the relay can say this: neither agent can declare a deal hopeless
|
|
276
|
+
without leaking its own number.
|
|
277
|
+
|
|
278
|
+
Limits are fixed once declared. A party that could move its bound and ask
|
|
279
|
+
again could find the other's number one bit at a time.
|
|
280
|
+
|
|
281
|
+
[`vectors/limits.json`](vectors/limits.json) has limits to refuse, terms to
|
|
282
|
+
check, and pairs that cannot meet.
|
|
283
|
+
|
|
284
|
+
### Circling
|
|
285
|
+
|
|
286
|
+
Without limits the relay can still see a stall. When the last four moves are
|
|
287
|
+
all `counter` or `need_more_detail`, and none of them tabled terms that were not
|
|
288
|
+
on the table before them, the room is **circling**. The relay SHOULD tell both
|
|
289
|
+
parties once, on the move that tips it. Any new terms, however small a
|
|
290
|
+
concession, count as progress. The relay MUST NOT close the room for it; the
|
|
291
|
+
humans decide. [`vectors/circling.json`](vectors/circling.json) has cases.
|
|
292
|
+
|
|
293
|
+
## 8. Human in the loop
|
|
294
|
+
|
|
295
|
+
`approve`, `reject` and `cancel` are never released by an agent alone. The relay
|
|
296
|
+
answers the agent with `pending_human`, the confirmation goes to the human's
|
|
297
|
+
channel, and only the human's response releases the move. A human who
|
|
298
|
+
negotiates in person (§10) acts for themselves, and their moves need no
|
|
299
|
+
second confirmation: this section exists to keep an agent from deciding alone.
|
|
300
|
+
|
|
301
|
+
The confirmation card renders **typed fields from the declared schema**, not
|
|
302
|
+
the agent's summary of them. If the human confirms a retelling, the
|
|
303
|
+
confirmation is theatre, and every injection that reaches the agent reaches
|
|
304
|
+
the decision. With limits (§7), the card marks each field that falls outside
|
|
305
|
+
them.
|
|
306
|
+
|
|
307
|
+
The channel is not part of the protocol; it sits behind a notifier interface.
|
|
308
|
+
It needs push, because §6 makes the room responsible for chasing its human.
|
|
309
|
+
The hosted Taasky relay uses Telegram; Slack fits a workspace.
|
|
310
|
+
|
|
311
|
+
Why a human is there differs by deployment. Across organisations, the other
|
|
312
|
+
side cannot be trusted. Inside one, trust is not the question — accountability
|
|
313
|
+
is: the approval is the record of who owned the decision.
|
|
314
|
+
|
|
315
|
+
## 9. MCP binding
|
|
316
|
+
|
|
317
|
+
Agents reach a relay as an MCP server, over Streamable HTTP. The tools:
|
|
318
|
+
|
|
319
|
+
| Tool | Does | Human |
|
|
320
|
+
| --- | --- | --- |
|
|
321
|
+
| `negotiation_open` | create a room: `subject_schema`, optional `limits`; returns an invite link | — |
|
|
322
|
+
| `negotiation_join` | enter by `invite_code`, with own `subject_schema` and optional `limits` | — |
|
|
323
|
+
| `negotiation_list` | the caller's rooms: state, whose turn, nearest deadline | — |
|
|
324
|
+
| `negotiation_read` | one room: state, every round, the terms on the table and their `subject_hash` | — |
|
|
325
|
+
| `negotiation_propose` | `offer` / `counter` / `need_more_detail`, with `subject` and optional `deadline` | — |
|
|
326
|
+
| `negotiation_respond` | `approve` (with `subject_hash`) / `reject` | **required** |
|
|
327
|
+
| `negotiation_cancel` | withdraw, out of turn if need be | **required** |
|
|
328
|
+
|
|
329
|
+
Every tool that returns the other party's content MUST mark it as counterparty
|
|
330
|
+
data, not instructions.
|
|
331
|
+
|
|
332
|
+
A relay MAY be used without any account. Then `negotiation_open` and
|
|
333
|
+
`negotiation_join` each return a **room token**, the agent's seat in that one
|
|
334
|
+
room, and every later call names the room by it. The token is the whole
|
|
335
|
+
credential and SHOULD be stored only as a hash. An agent that passes an earlier
|
|
336
|
+
room token (`previous_room_token`) when it opens or joins stays the same party,
|
|
337
|
+
so its human's confirmation channel carries over. A relay MAY also offer
|
|
338
|
+
accounts, through OAuth or a key, so that one human's agents share rooms and a
|
|
339
|
+
channel.
|
|
340
|
+
|
|
341
|
+
## 10. Rooms and invitations
|
|
342
|
+
|
|
343
|
+
The initiator opens a room and gets an invitation link, which it sends to the
|
|
344
|
+
other side out of band. Opening the link lets the other side's agent join, and
|
|
345
|
+
lands it directly in the negotiation. Nothing has to be registered before the
|
|
346
|
+
first useful action: the human is first asked for something when a move needs
|
|
347
|
+
their confirmation (§8), and binding the confirmation channel then is what
|
|
348
|
+
registers them, at the moment there is a real deal to confirm.
|
|
349
|
+
|
|
350
|
+
The invited side does not need an agent at all. A relay SHOULD let the
|
|
351
|
+
invitation open a page where a person joins the room themselves. That person
|
|
352
|
+
negotiates in the shape they were invited to: their schema is the initiator's,
|
|
353
|
+
and their terms are entered through a form built from it. Every network starts
|
|
354
|
+
with one side that has an agent and one that does not; requiring an agent on
|
|
355
|
+
both sides would cap it at people who already run one.
|
|
356
|
+
|
|
357
|
+
An invitation expires. The hosted Taasky relay gives it 72 hours.
|
|
358
|
+
|
|
359
|
+
## 11. Threat notes
|
|
360
|
+
|
|
361
|
+
- `subject` and schema labels are counterparty-authored. They never enter a
|
|
362
|
+
system prompt and are never treated as instructions.
|
|
363
|
+
- `from` is assigned by the relay. A sender-supplied identity field would be an
|
|
364
|
+
impersonation primitive.
|
|
365
|
+
- Strict validation means rejecting the whole message, never repairing it.
|
|
366
|
+
- Limits are fixed once declared, and "cannot meet" answers yes or no per
|
|
367
|
+
field, so neither side can probe for the other's bound.
|
|
368
|
+
- A model-based injection check is a filter, not a guarantee. The human
|
|
369
|
+
confirmation in §8 is the actual backstop.
|
|
370
|
+
|
|
371
|
+
## 12. Conformance
|
|
372
|
+
|
|
373
|
+
An implementation is conformant when it passes the files in
|
|
374
|
+
[`vectors/`](vectors). They are plain JSON, so an implementation in any
|
|
375
|
+
language can run them; [`test/vectors.test.ts`](test/vectors.test.ts) shows how
|
|
376
|
+
the reference implementation does. Refusals are checked by outcome, not by
|
|
377
|
+
message: the wording is the implementation's own, but a rejected subject names
|
|
378
|
+
the path of the first problem (`subject.price`, `subject.slots[0]`).
|
|
379
|
+
|
|
380
|
+
## 13. Open questions
|
|
381
|
+
|
|
382
|
+
1. Whether v1 needs a free-text `note` field. Starting without one.
|
|
383
|
+
2. Where sealing lands in the envelope when end-to-end encryption arrives.
|
|
384
|
+
3. Handles and discovery — deferred until there is a network to search.
|
|
385
|
+
4. More than two parties in a room.
|
|
386
|
+
5. A protocol-level way for the human, not only the agent, to set limits.
|