@duetso/agent 0.1.20
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 +189 -0
- package/README.md +315 -0
- package/dist/package.json +84 -0
- package/dist/src/cli.d.ts +23 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +754 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/core/serializer.d.ts +3 -0
- package/dist/src/core/serializer.d.ts.map +1 -0
- package/dist/src/core/serializer.js +22 -0
- package/dist/src/core/serializer.js.map +1 -0
- package/dist/src/core/structured-output.d.ts +13 -0
- package/dist/src/core/structured-output.d.ts.map +1 -0
- package/dist/src/core/structured-output.js +41 -0
- package/dist/src/core/structured-output.js.map +1 -0
- package/dist/src/guardrails/firewall.d.ts +7 -0
- package/dist/src/guardrails/firewall.d.ts.map +1 -0
- package/dist/src/guardrails/firewall.js +31 -0
- package/dist/src/guardrails/firewall.js.map +1 -0
- package/dist/src/guardrails/pattern.d.ts +13 -0
- package/dist/src/guardrails/pattern.d.ts.map +1 -0
- package/dist/src/guardrails/pattern.js +70 -0
- package/dist/src/guardrails/pattern.js.map +1 -0
- package/dist/src/guardrails/semantic.d.ts +14 -0
- package/dist/src/guardrails/semantic.d.ts.map +1 -0
- package/dist/src/guardrails/semantic.js +47 -0
- package/dist/src/guardrails/semantic.js.map +1 -0
- package/dist/src/index.d.ts +20 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +20 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lib/compact-json.d.ts +11 -0
- package/dist/src/lib/compact-json.d.ts.map +1 -0
- package/dist/src/lib/compact-json.js +36 -0
- package/dist/src/lib/compact-json.js.map +1 -0
- package/dist/src/lib/xml.d.ts +3 -0
- package/dist/src/lib/xml.d.ts.map +1 -0
- package/dist/src/lib/xml.js +9 -0
- package/dist/src/lib/xml.js.map +1 -0
- package/dist/src/memory/observation-groups.d.ts +15 -0
- package/dist/src/memory/observation-groups.d.ts.map +1 -0
- package/dist/src/memory/observation-groups.js +159 -0
- package/dist/src/memory/observation-groups.js.map +1 -0
- package/dist/src/memory/observational-prompts.d.ts +27 -0
- package/dist/src/memory/observational-prompts.d.ts.map +1 -0
- package/dist/src/memory/observational-prompts.js +237 -0
- package/dist/src/memory/observational-prompts.js.map +1 -0
- package/dist/src/memory/observational.d.ts +63 -0
- package/dist/src/memory/observational.d.ts.map +1 -0
- package/dist/src/memory/observational.js +605 -0
- package/dist/src/memory/observational.js.map +1 -0
- package/dist/src/memory/storage.d.ts +3 -0
- package/dist/src/memory/storage.d.ts.map +1 -0
- package/dist/src/memory/storage.js +127 -0
- package/dist/src/memory/storage.js.map +1 -0
- package/dist/src/memory/store.d.ts +13 -0
- package/dist/src/memory/store.d.ts.map +1 -0
- package/dist/src/memory/store.js +106 -0
- package/dist/src/memory/store.js.map +1 -0
- package/dist/src/model-resolution/duet-gateway.d.ts +35 -0
- package/dist/src/model-resolution/duet-gateway.d.ts.map +1 -0
- package/dist/src/model-resolution/duet-gateway.js +56 -0
- package/dist/src/model-resolution/duet-gateway.js.map +1 -0
- package/dist/src/model-resolution/index.d.ts +31 -0
- package/dist/src/model-resolution/index.d.ts.map +1 -0
- package/dist/src/model-resolution/index.js +129 -0
- package/dist/src/model-resolution/index.js.map +1 -0
- package/dist/src/session/session-manager.d.ts +45 -0
- package/dist/src/session/session-manager.d.ts.map +1 -0
- package/dist/src/session/session-manager.js +94 -0
- package/dist/src/session/session-manager.js.map +1 -0
- package/dist/src/session/session.d.ts +113 -0
- package/dist/src/session/session.d.ts.map +1 -0
- package/dist/src/session/session.js +308 -0
- package/dist/src/session/session.js.map +1 -0
- package/dist/src/tui/app.d.ts +60 -0
- package/dist/src/tui/app.d.ts.map +1 -0
- package/dist/src/tui/app.js +742 -0
- package/dist/src/tui/app.js.map +1 -0
- package/dist/src/turn-runner/agent-events.d.ts +5 -0
- package/dist/src/turn-runner/agent-events.d.ts.map +1 -0
- package/dist/src/turn-runner/agent-events.js +59 -0
- package/dist/src/turn-runner/agent-events.js.map +1 -0
- package/dist/src/turn-runner/prompts.d.ts +13 -0
- package/dist/src/turn-runner/prompts.d.ts.map +1 -0
- package/dist/src/turn-runner/prompts.js +79 -0
- package/dist/src/turn-runner/prompts.js.map +1 -0
- package/dist/src/turn-runner/shell-state-handle.d.ts +32 -0
- package/dist/src/turn-runner/shell-state-handle.d.ts.map +1 -0
- package/dist/src/turn-runner/shell-state-handle.js +168 -0
- package/dist/src/turn-runner/shell-state-handle.js.map +1 -0
- package/dist/src/turn-runner/skill-context.d.ts +26 -0
- package/dist/src/turn-runner/skill-context.d.ts.map +1 -0
- package/dist/src/turn-runner/skill-context.js +110 -0
- package/dist/src/turn-runner/skill-context.js.map +1 -0
- package/dist/src/turn-runner/skills.d.ts +35 -0
- package/dist/src/turn-runner/skills.d.ts.map +1 -0
- package/dist/src/turn-runner/skills.js +130 -0
- package/dist/src/turn-runner/skills.js.map +1 -0
- package/dist/src/turn-runner/state-machine-controller.d.ts +90 -0
- package/dist/src/turn-runner/state-machine-controller.d.ts.map +1 -0
- package/dist/src/turn-runner/state-machine-controller.js +289 -0
- package/dist/src/turn-runner/state-machine-controller.js.map +1 -0
- package/dist/src/turn-runner/state-machine-session.d.ts +27 -0
- package/dist/src/turn-runner/state-machine-session.d.ts.map +1 -0
- package/dist/src/turn-runner/state-machine-session.js +189 -0
- package/dist/src/turn-runner/state-machine-session.js.map +1 -0
- package/dist/src/turn-runner/tools.d.ts +193 -0
- package/dist/src/turn-runner/tools.d.ts.map +1 -0
- package/dist/src/turn-runner/tools.js +509 -0
- package/dist/src/turn-runner/tools.js.map +1 -0
- package/dist/src/turn-runner/turn-runner.d.ts +160 -0
- package/dist/src/turn-runner/turn-runner.d.ts.map +1 -0
- package/dist/src/turn-runner/turn-runner.js +907 -0
- package/dist/src/turn-runner/turn-runner.js.map +1 -0
- package/dist/src/turn-runner/turn-state.d.ts +6 -0
- package/dist/src/turn-runner/turn-state.d.ts.map +1 -0
- package/dist/src/turn-runner/turn-state.js +32 -0
- package/dist/src/turn-runner/turn-state.js.map +1 -0
- package/dist/src/turn-runner/usage-accounting.d.ts +7 -0
- package/dist/src/turn-runner/usage-accounting.d.ts.map +1 -0
- package/dist/src/turn-runner/usage-accounting.js +49 -0
- package/dist/src/turn-runner/usage-accounting.js.map +1 -0
- package/dist/src/types/agent.d.ts +15 -0
- package/dist/src/types/agent.d.ts.map +1 -0
- package/dist/src/types/agent.js +2 -0
- package/dist/src/types/agent.js.map +1 -0
- package/dist/src/types/config.d.ts +37 -0
- package/dist/src/types/config.d.ts.map +1 -0
- package/dist/src/types/config.js +2 -0
- package/dist/src/types/config.js.map +1 -0
- package/dist/src/types/guardrails.d.ts +34 -0
- package/dist/src/types/guardrails.d.ts.map +1 -0
- package/dist/src/types/guardrails.js +2 -0
- package/dist/src/types/guardrails.js.map +1 -0
- package/dist/src/types/memory.d.ts +151 -0
- package/dist/src/types/memory.d.ts.map +1 -0
- package/dist/src/types/memory.js +2 -0
- package/dist/src/types/memory.js.map +1 -0
- package/dist/src/types/protocol.d.ts +426 -0
- package/dist/src/types/protocol.d.ts.map +1 -0
- package/dist/src/types/protocol.js +2 -0
- package/dist/src/types/protocol.js.map +1 -0
- package/dist/src/types/state-machine.d.ts +344 -0
- package/dist/src/types/state-machine.d.ts.map +1 -0
- package/dist/src/types/state-machine.js +2 -0
- package/dist/src/types/state-machine.js.map +1 -0
- package/package.json +84 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work.
|
|
38
|
+
|
|
39
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
40
|
+
form, that is based on (or derived from) the Work and for which the
|
|
41
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
42
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
43
|
+
of this License, Derivative Works shall not include works that remain
|
|
44
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
45
|
+
the Work and Derivative Works thereof.
|
|
46
|
+
|
|
47
|
+
"Contribution" shall mean any work of authorship, including
|
|
48
|
+
the original version of the Work and any modifications or additions
|
|
49
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
50
|
+
submitted to the Licensor for inclusion in the Work by the copyright owner
|
|
51
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
52
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
53
|
+
means any form of electronic, verbal, or written communication sent
|
|
54
|
+
to the Licensor or its representatives, including but not limited to
|
|
55
|
+
communication on electronic mailing lists, source code control systems,
|
|
56
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
57
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
58
|
+
excluding communication that is conspicuously marked or otherwise
|
|
59
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
60
|
+
|
|
61
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
62
|
+
on behalf of whom a Contribution has been received by the Licensor and
|
|
63
|
+
subsequently incorporated within the Work.
|
|
64
|
+
|
|
65
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
66
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
67
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
68
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
69
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
70
|
+
Work and such Derivative Works in Source or Object form.
|
|
71
|
+
|
|
72
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
73
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
74
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
75
|
+
(except as stated in this section) patent license to make, have made,
|
|
76
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
77
|
+
where such license applies only to those patent claims licensable
|
|
78
|
+
by such Contributor that are necessarily infringed by their
|
|
79
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
80
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
81
|
+
institute patent litigation against any entity (including a
|
|
82
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
83
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
84
|
+
or contributory patent infringement, then any patent licenses
|
|
85
|
+
granted to You under this License for that Work shall terminate
|
|
86
|
+
as of the date such litigation is filed.
|
|
87
|
+
|
|
88
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
89
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
90
|
+
modifications, and in Source or Object form, provided that You
|
|
91
|
+
meet the following conditions:
|
|
92
|
+
|
|
93
|
+
(a) You must give any other recipients of the Work or
|
|
94
|
+
Derivative Works a copy of this License; and
|
|
95
|
+
|
|
96
|
+
(b) You must cause any modified files to carry prominent notices
|
|
97
|
+
stating that You changed the files; and
|
|
98
|
+
|
|
99
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
100
|
+
that You distribute, all copyright, patent, trademark, and
|
|
101
|
+
attribution notices from the Source form of the Work,
|
|
102
|
+
excluding those notices that do not pertain to any part of
|
|
103
|
+
the Derivative Works; and
|
|
104
|
+
|
|
105
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
106
|
+
distribution, then any Derivative Works that You distribute must
|
|
107
|
+
include a readable copy of the attribution notices contained
|
|
108
|
+
within such NOTICE file, excluding any notices that do not
|
|
109
|
+
pertain to any part of the Derivative Works, in at least one
|
|
110
|
+
of the following places: within a NOTICE text file distributed
|
|
111
|
+
as part of the Derivative Works; within the Source form or
|
|
112
|
+
documentation, if provided along with the Derivative Works; or,
|
|
113
|
+
within a display generated by the Derivative Works, if and
|
|
114
|
+
wherever such third-party notices normally appear. The contents
|
|
115
|
+
of the NOTICE file are for informational purposes only and
|
|
116
|
+
do not modify the License. You may add Your own attribution
|
|
117
|
+
notices within Derivative Works that You distribute, alongside
|
|
118
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
119
|
+
that such additional attribution notices cannot be construed
|
|
120
|
+
as modifying the License.
|
|
121
|
+
|
|
122
|
+
You may add Your own copyright statement to Your modifications and
|
|
123
|
+
may provide additional or different license terms and conditions
|
|
124
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
125
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
126
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
127
|
+
the conditions stated in this License.
|
|
128
|
+
|
|
129
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
130
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
131
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
132
|
+
this License, without any additional terms or conditions.
|
|
133
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
134
|
+
the terms of any separate license agreement you may have executed
|
|
135
|
+
with Licensor regarding such Contributions.
|
|
136
|
+
|
|
137
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
138
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
139
|
+
except as required for reasonable and customary use in describing the
|
|
140
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
141
|
+
|
|
142
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
143
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
144
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
145
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
146
|
+
implied, including, without limitation, any warranties or conditions
|
|
147
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
148
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
149
|
+
appropriateness of using or redistributing the Work and assume any
|
|
150
|
+
risks associated with Your exercise of permissions under this License.
|
|
151
|
+
|
|
152
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
153
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
154
|
+
unless required by applicable law (such as deliberate and grossly
|
|
155
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
156
|
+
liable to You for damages, including any direct, indirect, special,
|
|
157
|
+
incidental, or consequential damages of any character arising as a
|
|
158
|
+
result of this License or out of the use or inability to use the
|
|
159
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
160
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
161
|
+
other commercial damages or losses), even if such Contributor
|
|
162
|
+
has been advised of the possibility of such damages.
|
|
163
|
+
|
|
164
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
165
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
166
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
167
|
+
or other liability obligations and/or rights consistent with this
|
|
168
|
+
License. However, in accepting such obligations, You may act only
|
|
169
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
170
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
171
|
+
defend, and hold each Contributor harmless for any liability
|
|
172
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
173
|
+
of your accepting any such warranty or additional liability.
|
|
174
|
+
|
|
175
|
+
END OF TERMS AND CONDITIONS
|
|
176
|
+
|
|
177
|
+
Copyright 2026 Duet
|
|
178
|
+
|
|
179
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
180
|
+
you may not use this file except in compliance with the License.
|
|
181
|
+
You may obtain a copy of the License at
|
|
182
|
+
|
|
183
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
184
|
+
|
|
185
|
+
Unless required by applicable law or agreed to in writing, software
|
|
186
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
187
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
188
|
+
See the License for the specific language governing permissions and
|
|
189
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
# duet-agent
|
|
2
|
+
|
|
3
|
+
An opinionated, full-stack agent turn runner. Native memories. Native interrupts. Multi-agent by default.
|
|
4
|
+
|
|
5
|
+
**No MCP. Everything is files and CLI.**
|
|
6
|
+
|
|
7
|
+
## Why another agent framework?
|
|
8
|
+
|
|
9
|
+
Existing agent turn runners treat tools and memories as pluggable modules. This makes them flexible but fundamentally disconnected — memory is an afterthought.
|
|
10
|
+
|
|
11
|
+
duet-agent takes the opposite approach: **memory is woven into the core architecture.** An agent without memory is stateless. Interrupts are handled by the underlying pi agent runtime, so the turn runner does not need its own interrupt bus.
|
|
12
|
+
|
|
13
|
+
## Architecture
|
|
14
|
+
|
|
15
|
+
The diagram below walks through a realistic agent-routed state machine: outbound conference
|
|
16
|
+
outreach. The user prompt enters the `TurnRunner`, the runner agent picks the next state based on
|
|
17
|
+
prompt, history, and available state definitions, and the state machine drives the business process
|
|
18
|
+
until it hits a terminal state. The same definition can start in the middle — for example, the
|
|
19
|
+
runner can skip straight to `wait_for_reply` if the user says “I already emailed them”.
|
|
20
|
+
|
|
21
|
+
```mermaid
|
|
22
|
+
stateDiagram-v2
|
|
23
|
+
direction TB
|
|
24
|
+
|
|
25
|
+
[*] --> Classify : user prompt
|
|
26
|
+
Classify --> AgentMode : one-off task
|
|
27
|
+
Classify --> Outreach : matches a state machine
|
|
28
|
+
|
|
29
|
+
AgentMode --> [*] : answer / edits
|
|
30
|
+
|
|
31
|
+
state Outreach {
|
|
32
|
+
direction TB
|
|
33
|
+
[*] --> research_prospect
|
|
34
|
+
|
|
35
|
+
research_prospect : research_prospect (agent)\nweb + notes lookup
|
|
36
|
+
draft_email : draft_email (agent)\nwrite first-touch email
|
|
37
|
+
send_email : send_email (script)\nbash: gmail send
|
|
38
|
+
wait_for_reply : wait_for_reply (poll)\nevery 6h: check inbox
|
|
39
|
+
schedule_meeting : schedule_meeting (script)\nbash: calendly create
|
|
40
|
+
meeting_booked : meeting_booked (terminal: completed)
|
|
41
|
+
not_interested : not_interested (terminal: completed)
|
|
42
|
+
no_response : no_response (terminal: cancelled)
|
|
43
|
+
|
|
44
|
+
research_prospect --> draft_email : enough signal
|
|
45
|
+
research_prospect --> not_interested : disqualified
|
|
46
|
+
draft_email --> send_email
|
|
47
|
+
send_email --> wait_for_reply
|
|
48
|
+
wait_for_reply --> schedule_meeting : positive reply
|
|
49
|
+
wait_for_reply --> not_interested : declined
|
|
50
|
+
wait_for_reply --> no_response : 14d timeout
|
|
51
|
+
schedule_meeting --> meeting_booked
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
Outreach --> [*]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Each state is one of the four kinds the runner understands:
|
|
58
|
+
|
|
59
|
+
- **agent** states run a sub-agent with a prompt, optional system prompt, and optional skill allowlist.
|
|
60
|
+
- **script** states shell out (`bash`, `curl`, CLIs) for anything with an API.
|
|
61
|
+
- **poll** states wait on an external signal by running one script check per interval, or by using a timer poll for pure delays.
|
|
62
|
+
- **terminal** states record a business outcome (`completed`, `cancelled`, `failed`).
|
|
63
|
+
|
|
64
|
+
Observational memory, pi coding tools, and guardrails sit underneath every state transition; they
|
|
65
|
+
are not states themselves.
|
|
66
|
+
|
|
67
|
+
## Key Differentiators
|
|
68
|
+
|
|
69
|
+
### Native Memory
|
|
70
|
+
|
|
71
|
+
Memory is first-class. The default `MemoryStore` is in-memory and emits observation events; optional PGlite storage hydrates and persists durable observations outside the turn runner session.
|
|
72
|
+
|
|
73
|
+
The memory model follows observational memory: turn runner session messages are observed into durable text observations, and a reflector condenses observations when they grow too large. Observations are scoped as `session` or `resource`.
|
|
74
|
+
|
|
75
|
+
### Pi Coding Tools
|
|
76
|
+
|
|
77
|
+
Sub-agents use the default tools from `@earendil-works/pi-coding-agent`: read, bash, edit, and write. The turn runner supplies a working directory and can restrict which skills are injected into a state-machine agent state; it does not wrap those tools in a second sandbox abstraction.
|
|
78
|
+
|
|
79
|
+
### Native Interrupts
|
|
80
|
+
|
|
81
|
+
Interrupt behavior comes from the underlying pi agent runtime. A user can send a message while a pi session is running, and the runtime can handle it as an interruption or as a follow-up. duet-agent does not add a second interrupt bus on top.
|
|
82
|
+
|
|
83
|
+
### Multi-Agent by Default
|
|
84
|
+
|
|
85
|
+
The turn runner can delegate durable process steps into agent states. Agent states are not pre-built classes; they are state-machine states with prompts, optional system prompts, and optional skill allowlists.
|
|
86
|
+
|
|
87
|
+
### Three Execution Modes
|
|
88
|
+
|
|
89
|
+
The turn runner has three top-level modes:
|
|
90
|
+
|
|
91
|
+
- `agent`: handle the prompt as a normal agent session. This is for one-off tasks, coding requests, reviews, research, and anything that can complete in the current session.
|
|
92
|
+
- `state_machine`: route the prompt into an agent-routed state machine. This is for long-running business processes with durable state, waits, and terminal business outcomes.
|
|
93
|
+
- `auto`: let the turn runner classify the prompt and choose either `agent` or `state_machine`.
|
|
94
|
+
|
|
95
|
+
Normal agent mode handles immediate work; state-machine mode handles business processes that may pause, resume, wait on external systems, or start in the middle based on the user's prompt. In `auto`, the runner classifies the prompt and routes to whichever fits.
|
|
96
|
+
|
|
97
|
+
### Agent-Routed State Machines
|
|
98
|
+
|
|
99
|
+
duet-agent is exploring long-running agent-routed state machines for business processes like outbound sales, conference outreach, and development loops. The design goal is **not** to become a workflow engine like Temporal, Airflow, or GitHub Actions.
|
|
100
|
+
|
|
101
|
+
Instead, state machines are agent-routed. A state-machine definition describes the available business states: agent states, shell-script states, poll states, and terminal states. The runner keeps the state-machine system prompt cache-friendly by including only stable routing instructions plus the original prompt and available state definitions. Current state and history stay in the parent agent conversation, where state transitions, script results, poll results, and user follow-ups are already recorded.
|
|
102
|
+
|
|
103
|
+
The state machine is higher level than task execution. It tracks one current business state at a time. If a state needs fan-out, parallelism, or a task-level workflow, that belongs inside an agent or script state. The agent can execute a complex workflow internally; the state machine only records the business transition before and after that state.
|
|
104
|
+
|
|
105
|
+
This keeps state machines flexible enough to start in the middle. For example, a user can say: "prospect person X, I've already sent email, just wait for response." The same outreach state machine can skip research and email sending, then choose the wait-for-response state because the runner agent understands the existing context.
|
|
106
|
+
|
|
107
|
+
External integrations stay simple: anything with an API or CLI is a script state or script poll. Timer polls cover pure delays such as "wait before retry." Email, GitHub, Calendly, CRM systems, and webhooks do not need first-class engine concepts. If the state machine can tolerate a few minutes of polling delay, a bash script is enough.
|
|
108
|
+
|
|
109
|
+
What this is not:
|
|
110
|
+
|
|
111
|
+
- Not a deterministic DAG scheduler.
|
|
112
|
+
- Not a low-level durable execution runtime.
|
|
113
|
+
- Not a workflow service with queues, workers, locks, and retries as the main abstraction.
|
|
114
|
+
- Not a replacement for infrastructure workflow engines when exact-once execution or strict SLAs matter.
|
|
115
|
+
|
|
116
|
+
The turn runner should provide enough structure for an agent to make good process decisions, while leaving hard operational guarantees to external systems.
|
|
117
|
+
|
|
118
|
+
### Optional Guardrails
|
|
119
|
+
|
|
120
|
+
Pattern-based (fast, regex) and semantic (LLM-evaluated) guardrails compose into a firewall. Every bash command and file write can be checked before execution.
|
|
121
|
+
|
|
122
|
+
## CLI Install
|
|
123
|
+
|
|
124
|
+
The CLI runs on Bun because OpenTUI is Bun-native. Install Bun first if it is not already available:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
curl -fsSL https://bun.sh/install | bash
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Install the CLI globally to make the `duet` command available on your PATH:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
bun add --global @duetso/agent
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
You can also install it globally with another package manager:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npm install --global @duetso/agent
|
|
140
|
+
pnpm add --global @duetso/agent
|
|
141
|
+
yarn global add @duetso/agent
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Upgrade an existing global installation:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
duet upgrade
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## SDK Install
|
|
151
|
+
|
|
152
|
+
Install the package as a dependency when you want to use the turn runner from TypeScript or JavaScript:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
npm install @duetso/agent
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Development
|
|
159
|
+
|
|
160
|
+
This repo uses Bun for package management, Husky for pre-commit checks, and Docker for functional tests.
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
bun install
|
|
164
|
+
bun run setup # install/start Docker on macOS or Linux if needed
|
|
165
|
+
bun run check-types
|
|
166
|
+
bun run lint
|
|
167
|
+
bun run eval # runs live evals inside Docker
|
|
168
|
+
bun run test # runs the test suite inside Docker
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Use `bun run test` and `bun run eval`, not raw `bun test`, as the source of truth. File-writing tests and evals run in Docker so focused host runs cannot create `.duet`, PGlite databases, or home-directory skill fixtures in the checkout.
|
|
172
|
+
|
|
173
|
+
The pre-commit hook runs `format`, `check-types`, and `lint`.
|
|
174
|
+
|
|
175
|
+
## CLI Quick Start
|
|
176
|
+
|
|
177
|
+
Set a provider API key in the environment or in `<workdir>/.env`, then run `duet` from any project directory. When `--model` is omitted, the CLI infers a default from the configured provider: Anthropic, AI Gateway, and OpenRouter use Opus 4.7; OpenAI uses GPT-5.5.
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
export ANTHROPIC_API_KEY=sk-...
|
|
181
|
+
|
|
182
|
+
# Start a session
|
|
183
|
+
duet "build a REST API with Express"
|
|
184
|
+
|
|
185
|
+
# Open an interactive session without an initial prompt
|
|
186
|
+
duet
|
|
187
|
+
|
|
188
|
+
# With options
|
|
189
|
+
duet -m anthropic:claude-opus-4-7 --workdir ./my-project "refactor the auth module"
|
|
190
|
+
|
|
191
|
+
# With a custom observational memory model
|
|
192
|
+
duet --memory-model anthropic:claude-sonnet-4-6 "summarize this repo"
|
|
193
|
+
|
|
194
|
+
# With additional system instructions
|
|
195
|
+
duet --system-prompt "Prefer concise answers." "review this repo"
|
|
196
|
+
|
|
197
|
+
# Override the default AGENTS.md system prompt file
|
|
198
|
+
duet --system-prompt-file TEAM.md "review this repo"
|
|
199
|
+
|
|
200
|
+
# Disable system prompt file loading
|
|
201
|
+
duet --no-system-prompt-files "review this repo"
|
|
202
|
+
|
|
203
|
+
# Resume a saved session
|
|
204
|
+
duet --resume session_abc123 --workdir ./my-project
|
|
205
|
+
|
|
206
|
+
# Through Vercel AI Gateway
|
|
207
|
+
export AI_GATEWAY_API_KEY=...
|
|
208
|
+
duet -m vercel-ai-gateway:anthropic/claude-opus-4.7 "review this repo"
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
For local development from a checkout, use the package script:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
bun run cli -- "build a REST API with Express"
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## SDK Quick Start
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
import { TurnRunner } from "@duetso/agent";
|
|
221
|
+
|
|
222
|
+
const turnRunner = new TurnRunner({
|
|
223
|
+
model: "anthropic:claude-opus-4-7",
|
|
224
|
+
cwd: process.cwd(),
|
|
225
|
+
mode: "auto",
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const terminal = await turnRunner.turn({
|
|
229
|
+
type: "start",
|
|
230
|
+
prompt: "Build a todo app with React and TypeScript",
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`TurnRunner.turn()` is the concurrency boundary. Callers may call it repeatedly
|
|
235
|
+
while work is active; the runner folds active `prompt` and `answer` commands
|
|
236
|
+
back into the active pi agent as `steer` or `follow_up`, queues wakes and other
|
|
237
|
+
work it cannot absorb immediately, and emits one terminal event when the whole
|
|
238
|
+
active work chain is done. The parent runner transcript stays linear: state
|
|
239
|
+
machine continuations, script results, poll results, and user follow-ups rejoin
|
|
240
|
+
the parent agent rather than creating separate conversation branches.
|
|
241
|
+
|
|
242
|
+
## Memory And Persistence
|
|
243
|
+
|
|
244
|
+
duet-agent owns a concrete event-emitting `MemoryStore` internally. It is the runtime state container, not a database adapter.
|
|
245
|
+
|
|
246
|
+
`SessionManager` stores session snapshots under `~/.duet/sessions` by default and enables durable observational memory at `~/.duet/memory.db`. Pass `memoryDbPath: false` to keep observational memory in process only, or provide `memoryDbPath` for a custom database location.
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
import { SessionManager } from "@duetso/agent";
|
|
250
|
+
|
|
251
|
+
const manager = new SessionManager({
|
|
252
|
+
model: "anthropic:claude-opus-4-7",
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The memory module hydrates durable observations from an embedded Postgres database powered by PGlite before the first turn and writes observation updates back as memory changes. Raw conversation messages stay in `TurnState.agent.messages`; memory persistence stores only derived observations/reflections.
|
|
257
|
+
|
|
258
|
+
You can also resume directly from saved state:
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
const terminal = await turnRunner.turn({
|
|
262
|
+
type: "prompt",
|
|
263
|
+
state: savedState,
|
|
264
|
+
message: "Continue the previous goal",
|
|
265
|
+
behavior: "follow_up",
|
|
266
|
+
});
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Resume continues turn runner session state, not an in-flight model/tool call. Any `in_progress` todo is retried from `pending`.
|
|
270
|
+
|
|
271
|
+
Observational memory is enabled by default with thresholds tuned for modern 200k-token model windows:
|
|
272
|
+
|
|
273
|
+
- Raw messages are observed around `150_000` tokens so exact transcript context and prompt caching are used before compaction.
|
|
274
|
+
- Observation logs are reflected around `90_000` tokens, targeting about `65_000` tokens after reflection.
|
|
275
|
+
- Raw-tail retention keeps about `40_000` exact message tokens after observation activation.
|
|
276
|
+
- Observation context is injected as reminder messages; replacing raw context with observations/reflections is the compaction path.
|
|
277
|
+
|
|
278
|
+
## Skills
|
|
279
|
+
|
|
280
|
+
Skills are loaded from `<cwd>/.duet/skills`, `<cwd>/.agents/skills`, `~/.duet/skills`, and `~/.agents/skills` by default, using `@earendil-works/pi-coding-agent`'s skill loader. The turn runner injects every loaded skill's description and instructions into the agent system prompt. `getSkills()` returns the discovered skills, including YAML frontmatter descriptions such as block scalars.
|
|
281
|
+
|
|
282
|
+
## Guardrails
|
|
283
|
+
|
|
284
|
+
The turn runner installs its default safety checks internally. Add extra guardrail config objects when a deployment needs stricter local policy.
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
const turnRunner = new TurnRunner({
|
|
288
|
+
// ...
|
|
289
|
+
guardrails: [
|
|
290
|
+
{
|
|
291
|
+
kind: "pattern",
|
|
292
|
+
rules: [
|
|
293
|
+
{ pattern: /production-db/i, action: "warn", reason: "Production database mentioned" },
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
kind: "semantic",
|
|
298
|
+
model: getModel("anthropic", "claude-haiku-4-5"),
|
|
299
|
+
policy: "Never delete production data. Never expose secrets in output.",
|
|
300
|
+
},
|
|
301
|
+
],
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Design Principles
|
|
306
|
+
|
|
307
|
+
1. **Files and CLI over protocols.** No MCP, no custom APIs. If you can't do it with bash, you can't do it.
|
|
308
|
+
2. **Runtime state over persistence.** The turn runner owns in-memory state and emits events. Persistence lives in external modules or initial-state hydration.
|
|
309
|
+
3. **Agent-routed state machines over workflow engines.** Long-running state machines describe available business states; a runner agent decides what to do next from prompt, state, and history. Task-level workflows belong inside agent or script states.
|
|
310
|
+
4. **Dynamic over static.** Agent states are defined by state machines at runtime, not pre-built classes.
|
|
311
|
+
5. **Simple over flexible.** Default pi coding tools. One default memory store. Constraints breed creativity.
|
|
312
|
+
|
|
313
|
+
## License
|
|
314
|
+
|
|
315
|
+
Apache-2.0
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@duetso/agent",
|
|
3
|
+
"version": "0.1.20",
|
|
4
|
+
"description": "An opinionated full-stack agent turn runner with native memories, interrupts, and multi-agent orchestration",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agent",
|
|
7
|
+
"ai",
|
|
8
|
+
"llm",
|
|
9
|
+
"memory",
|
|
10
|
+
"multi-agent",
|
|
11
|
+
"session-manager"
|
|
12
|
+
],
|
|
13
|
+
"license": "Apache-2.0",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/dzhng/duet-agent"
|
|
17
|
+
},
|
|
18
|
+
"bin": {
|
|
19
|
+
"duet": "dist/src/cli.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist/package.json",
|
|
23
|
+
"dist/src",
|
|
24
|
+
"LICENSE",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"main": "dist/src/index.js",
|
|
29
|
+
"types": "dist/src/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/src/index.d.ts",
|
|
33
|
+
"import": "./dist/src/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./package.json": "./package.json"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"setup": "bash scripts/setup-docker.sh",
|
|
42
|
+
"build": "tsc",
|
|
43
|
+
"check-types": "tsc --noEmit",
|
|
44
|
+
"cli": "bun src/cli.ts",
|
|
45
|
+
"dev": "tsc --watch",
|
|
46
|
+
"eval": "docker run --rm -v \"$PWD:/src:ro\" -w /work -e HOME=/tmp/home -e DUET_TEST_IN_DOCKER=1 oven/bun:1.3.11 sh -lc 'cp -R /src/. /work && bun install --frozen-lockfile && bun test ./evals/*.eval.ts'",
|
|
47
|
+
"format": "oxfmt --write --no-error-on-unmatched-pattern",
|
|
48
|
+
"format:check": "oxfmt --check --no-error-on-unmatched-pattern",
|
|
49
|
+
"lint": "oxlint src/",
|
|
50
|
+
"prepack": "bun run build",
|
|
51
|
+
"test": "docker run --rm -v \"$PWD:/src:ro\" -w /work -e HOME=/tmp/home -e DUET_TEST_IN_DOCKER=1 oven/bun:1.3.11 sh -lc 'cp -R /src/. /work && bun install --frozen-lockfile && bun test ./test/*.test.ts'",
|
|
52
|
+
"example": "bun examples/basic.ts",
|
|
53
|
+
"example:state-machine": "bun examples/state-machine.ts",
|
|
54
|
+
"prepare": "husky",
|
|
55
|
+
"build:compile": "bun build src/cli.ts --compile --outfile dist/duet"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@earendil-works/pi-agent-core": "^0.74.0",
|
|
59
|
+
"@earendil-works/pi-ai": "^0.74.0",
|
|
60
|
+
"@earendil-works/pi-coding-agent": "^0.74.0",
|
|
61
|
+
"@electric-sql/pglite": "^0.4.5",
|
|
62
|
+
"@opentui/core": "^0.2.2",
|
|
63
|
+
"ajv": "^8.20.0",
|
|
64
|
+
"dedent": "^1.7.2",
|
|
65
|
+
"dotenv": "^17.4.2",
|
|
66
|
+
"eventemitter3": "^5.0.0",
|
|
67
|
+
"jstoxml": "^7.1.0",
|
|
68
|
+
"nanoid": "^5.0.0",
|
|
69
|
+
"typebox": "^1.1.24"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@types/bun": "^1.3.13",
|
|
73
|
+
"@types/node": "^22.0.0",
|
|
74
|
+
"husky": "^9.1.7",
|
|
75
|
+
"oxfmt": "^0.47.0",
|
|
76
|
+
"oxlint": "^0.16.0",
|
|
77
|
+
"tsx": "^4.19.0",
|
|
78
|
+
"typescript": "^5.7.0"
|
|
79
|
+
},
|
|
80
|
+
"engines": {
|
|
81
|
+
"node": ">=20"
|
|
82
|
+
},
|
|
83
|
+
"packageManager": "bun@1.3.11"
|
|
84
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* duet CLI
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* duet "build a todo app in React"
|
|
7
|
+
* duet --model claude-opus-4-7 "refactor auth system"
|
|
8
|
+
* echo "fix the bug in server.ts" | duet
|
|
9
|
+
*/
|
|
10
|
+
declare const PACKAGE_MANAGERS: readonly ["npm", "bun", "pnpm", "yarn"];
|
|
11
|
+
type PackageManager = (typeof PACKAGE_MANAGERS)[number];
|
|
12
|
+
type PackageManagerDetectionContext = {
|
|
13
|
+
userAgent?: string;
|
|
14
|
+
runtimeExecutable?: string;
|
|
15
|
+
cliFilePath?: string;
|
|
16
|
+
scriptPath?: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function parseResumeHistoryLines(value: string, optionName?: string): number;
|
|
19
|
+
export declare function formatNewVersionNotice(packageName: string, currentVersion: string, latestVersion: string): string;
|
|
20
|
+
export declare function compareSemverVersions(left: string, right: string): number;
|
|
21
|
+
export declare function detectPackageManagerFromContext(context: PackageManagerDetectionContext): PackageManager;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;GAOG;AAwBH,QAAA,MAAM,gBAAgB,yCAA0C,CAAC;AAEjE,KAAK,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD,KAAK,8BAA8B,GAAG;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAqZF,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,EACb,UAAU,SAA2B,GACpC,MAAM,CAKR;AAeD,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,GACpB,MAAM,CAER;AAmBD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAWzE;AA+FD,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,8BAA8B,GACtC,cAAc,CAiBhB"}
|