@agentproto/agencies 0.1.0-alpha.0
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/AGENTAGENCIES.md +39 -0
- package/LICENSE +21 -0
- package/README.md +90 -0
- package/dist/capacity-98VwY285.d.ts +862 -0
- package/dist/chunk-DYQ4A5HV.mjs +649 -0
- package/dist/chunk-DYQ4A5HV.mjs.map +1 -0
- package/dist/chunk-GAJQ5ZC2.mjs +149 -0
- package/dist/chunk-GAJQ5ZC2.mjs.map +1 -0
- package/dist/chunk-SOJ4GQR7.mjs +3 -0
- package/dist/chunk-SOJ4GQR7.mjs.map +1 -0
- package/dist/composition/index.d.ts +61 -0
- package/dist/composition/index.mjs +75 -0
- package/dist/composition/index.mjs.map +1 -0
- package/dist/doctypes/index.d.ts +61 -0
- package/dist/doctypes/index.mjs +4 -0
- package/dist/doctypes/index.mjs.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.mjs +17 -0
- package/dist/index.mjs.map +1 -0
- package/dist/renderers/index.d.ts +288 -0
- package/dist/renderers/index.mjs +321 -0
- package/dist/renderers/index.mjs.map +1 -0
- package/dist/validators/index.d.ts +70 -0
- package/dist/validators/index.mjs +4 -0
- package/dist/validators/index.mjs.map +1 -0
- package/package.json +94 -0
- package/src/spec/canvakit-templates/agency.agency-overview/template.canvakit.html +173 -0
- package/src/spec/canvakit-templates/agency.agency-profile/template.canvakit.html +121 -0
- package/src/spec/canvakit-templates/agency.agreement-signing/template.canvakit.html +164 -0
- package/src/spec/canvakit-templates/agency.deliverable-review/template.canvakit.html +148 -0
- package/src/spec/canvakit-templates/agency.engagement-dashboard/template.canvakit.html +191 -0
- package/src/spec/canvakit-templates/agency.invoice-pdf/template.canvakit.html +142 -0
- package/src/spec/canvakit-templates/agency.procedure-card/template.canvakit.html +93 -0
- package/src/spec/snippets/agency-overview-rollup/procedures/compute-agency-overview/PROCEDURE.md +88 -0
- package/src/spec/snippets/agency-overview-rollup/routines/agency-overview-rollup/ROUTINE.md +58 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
template: true
|
|
3
|
+
name: agency.agreement-signing
|
|
4
|
+
version: 1.0.0-alpha
|
|
5
|
+
description: |
|
|
6
|
+
Multi-party signing portal for AGREEMENT.md. Differs from
|
|
7
|
+
governance.signing-portal in three ways:
|
|
8
|
+
1. Renders the agreement body (line items, payment terms, scope) above the form.
|
|
9
|
+
2. Shows the multi-party signature status — already-signed parties are listed.
|
|
10
|
+
3. Knows about the agencies/v1 vocabulary (counterparty / operator / agent).
|
|
11
|
+
|
|
12
|
+
The host application:
|
|
13
|
+
- Reads AGREEMENT.md, computes documentHash, builds the signer list.
|
|
14
|
+
- Generates a single-use nonce + signing URL per pending signer.
|
|
15
|
+
- Receives the form POST and writes signature.json via signArtifactTool.
|
|
16
|
+
author: agencies.sh
|
|
17
|
+
variables:
|
|
18
|
+
agencyName: "Agency"
|
|
19
|
+
agreementTitle: "Agreement"
|
|
20
|
+
agreementPath: "engagements/example/AGREEMENT.md"
|
|
21
|
+
agreementBodyHtml: ""
|
|
22
|
+
documentHash: "0000000000000000000000000000000000000000000000000000000000000000"
|
|
23
|
+
signerKind: "counterparty"
|
|
24
|
+
signerSlug: "example-corp"
|
|
25
|
+
signerName: ""
|
|
26
|
+
signerEmail: ""
|
|
27
|
+
nonce: ""
|
|
28
|
+
signUrl: "/api/agency/sign-agreement"
|
|
29
|
+
alreadySignedParties: []
|
|
30
|
+
pendingParties: []
|
|
31
|
+
lineItems: []
|
|
32
|
+
totalFormatted: "0.00"
|
|
33
|
+
currency: "EUR"
|
|
34
|
+
paymentTermsHtml: ""
|
|
35
|
+
sources: {}
|
|
36
|
+
---
|
|
37
|
+
<!doctype html>
|
|
38
|
+
<html lang="en">
|
|
39
|
+
<head>
|
|
40
|
+
<meta charset="utf-8" />
|
|
41
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
42
|
+
<title>Sign: {{agreementTitle}} · {{agencyName}}</title>
|
|
43
|
+
<style>
|
|
44
|
+
:root { --fg: #1a1a1a; --muted: #666; --bg: #faf9f7; --panel: #fff; --border: #e5e5e5; --accent: #1a1a1a; --ok: #0a7d30; --warn: #b45309; }
|
|
45
|
+
body { font: 15px/1.55 ui-sans-serif, system-ui, sans-serif; max-width: 44rem; margin: 0 auto; padding: 2.5rem 1.5rem; color: var(--fg); background: var(--bg); }
|
|
46
|
+
h1 { font-size: 1.5rem; letter-spacing: -0.01em; margin: 0 0 0.25rem; }
|
|
47
|
+
.lead { color: var(--muted); margin: 0 0 2rem; font-size: 0.9375rem; }
|
|
48
|
+
.panel { background: var(--panel); border: 1px solid var(--border); border-radius: 8px; padding: 1.5rem; margin-bottom: 1.25rem; }
|
|
49
|
+
.panel h2 { font-size: 0.8125rem; text-transform: uppercase; letter-spacing: 0.08em; color: #666; margin: 0 0 0.875rem; }
|
|
50
|
+
.body { font-size: 0.9375rem; line-height: 1.6; color: #222; }
|
|
51
|
+
.body :is(p, ul, ol, blockquote) { margin: 0 0 0.75rem; }
|
|
52
|
+
table.lines { width: 100%; border-collapse: collapse; margin: 0; }
|
|
53
|
+
table.lines th { text-align: left; font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); padding: 0.5rem 0.75rem; border-bottom: 1.5px solid var(--accent); font-weight: 600; }
|
|
54
|
+
table.lines td { padding: 0.5rem 0.75rem; border-bottom: 1px solid #efece5; }
|
|
55
|
+
table.lines td.num { text-align: right; font-variant-numeric: tabular-nums; }
|
|
56
|
+
.total { display: flex; justify-content: space-between; margin-top: 0.75rem; padding-top: 0.75rem; border-top: 2px solid var(--accent); font-weight: 600; font-size: 1.0625rem; }
|
|
57
|
+
.signers { display: grid; gap: 0.5rem; }
|
|
58
|
+
.signer { display: flex; justify-content: space-between; align-items: center; padding: 0.625rem 0.875rem; background: #f7f5ee; border-radius: 6px; font-size: 0.875rem; }
|
|
59
|
+
.signer .who { font-weight: 500; }
|
|
60
|
+
.signer .when { font-family: ui-monospace, SF Mono, monospace; font-size: 0.8125rem; color: var(--muted); }
|
|
61
|
+
.pill { display: inline-block; padding: 0.125rem 0.5rem; border-radius: 999px; font-size: 0.6875rem; font-weight: 500; vertical-align: middle; }
|
|
62
|
+
.pill.ok { background: #e7f5eb; color: var(--ok); }
|
|
63
|
+
.pill.warn { background: #fef3c7; color: var(--warn); }
|
|
64
|
+
.field { margin-bottom: 1rem; }
|
|
65
|
+
label { display: block; font-size: 0.875rem; font-weight: 500; margin-bottom: 0.375rem; }
|
|
66
|
+
input { width: 100%; padding: 0.625rem 0.75rem; border: 1px solid #d4d4d4; border-radius: 6px; font: inherit; box-sizing: border-box; background: #fff; }
|
|
67
|
+
input:focus { outline: 2px solid #2563eb; outline-offset: -2px; }
|
|
68
|
+
button { width: 100%; padding: 0.75rem 1rem; background: var(--accent); color: #fff; border: 0; border-radius: 6px; font: inherit; font-weight: 500; cursor: pointer; }
|
|
69
|
+
button:hover { background: #000; }
|
|
70
|
+
.meta { font-size: 0.8125rem; color: var(--muted); padding: 0.75rem 0.875rem; background: #f5f4f0; border: 1px solid var(--border); border-radius: 6px; margin-bottom: 1rem; }
|
|
71
|
+
.meta dl { margin: 0; display: grid; grid-template-columns: max-content 1fr; gap: 0.25rem 0.75rem; }
|
|
72
|
+
.meta dt { font-weight: 500; color: #444; }
|
|
73
|
+
.meta dd { margin: 0; font-family: ui-monospace, SF Mono, monospace; font-size: 0.75rem; word-break: break-all; }
|
|
74
|
+
.legal { font-size: 0.8125rem; color: #777; line-height: 1.6; margin-top: 1rem; }
|
|
75
|
+
.legal em { font-style: normal; font-weight: 500; color: var(--fg); }
|
|
76
|
+
</style>
|
|
77
|
+
</head>
|
|
78
|
+
<body>
|
|
79
|
+
<p class="lead">{{agencyName}} · agreement signing</p>
|
|
80
|
+
<h1>{{agreementTitle}}</h1>
|
|
81
|
+
|
|
82
|
+
{{#agreementBodyHtml}}
|
|
83
|
+
<section class="panel body">
|
|
84
|
+
{{{agreementBodyHtml}}}
|
|
85
|
+
</section>
|
|
86
|
+
{{/agreementBodyHtml}}
|
|
87
|
+
|
|
88
|
+
{{#lineItems.length}}
|
|
89
|
+
<section class="panel">
|
|
90
|
+
<h2>Line items</h2>
|
|
91
|
+
<table class="lines">
|
|
92
|
+
<thead><tr><th>Description</th><th class="num">Qty</th><th class="num">Total</th></tr></thead>
|
|
93
|
+
<tbody>
|
|
94
|
+
{{#lineItems}}
|
|
95
|
+
<tr><td>{{description}}</td><td class="num">{{quantity}}</td><td class="num">{{totalFormatted}}</td></tr>
|
|
96
|
+
{{/lineItems}}
|
|
97
|
+
</tbody>
|
|
98
|
+
</table>
|
|
99
|
+
<div class="total"><span>Total</span><span>{{totalFormatted}} {{currency}}</span></div>
|
|
100
|
+
{{#paymentTermsHtml}}<div style="margin-top:0.875rem;color:var(--muted);font-size:0.875rem;">{{{paymentTermsHtml}}}</div>{{/paymentTermsHtml}}
|
|
101
|
+
</section>
|
|
102
|
+
{{/lineItems.length}}
|
|
103
|
+
|
|
104
|
+
{{#alreadySignedParties.length}}
|
|
105
|
+
<section class="panel">
|
|
106
|
+
<h2>Already signed</h2>
|
|
107
|
+
<div class="signers">
|
|
108
|
+
{{#alreadySignedParties}}
|
|
109
|
+
<div class="signer">
|
|
110
|
+
<span class="who">{{label}} <span class="pill ok">signed</span></span>
|
|
111
|
+
<span class="when">{{signedAt}}</span>
|
|
112
|
+
</div>
|
|
113
|
+
{{/alreadySignedParties}}
|
|
114
|
+
</div>
|
|
115
|
+
</section>
|
|
116
|
+
{{/alreadySignedParties.length}}
|
|
117
|
+
|
|
118
|
+
{{#pendingParties.length}}
|
|
119
|
+
<section class="panel">
|
|
120
|
+
<h2>Pending signatures</h2>
|
|
121
|
+
<div class="signers">
|
|
122
|
+
{{#pendingParties}}
|
|
123
|
+
<div class="signer">
|
|
124
|
+
<span class="who">{{label}} <span class="pill warn">pending</span></span>
|
|
125
|
+
<span class="when">{{deadline}}</span>
|
|
126
|
+
</div>
|
|
127
|
+
{{/pendingParties}}
|
|
128
|
+
</div>
|
|
129
|
+
</section>
|
|
130
|
+
{{/pendingParties.length}}
|
|
131
|
+
|
|
132
|
+
<section class="panel">
|
|
133
|
+
<h2>Your signature</h2>
|
|
134
|
+
<p style="margin:0 0 1rem;color:var(--muted);font-size:0.875rem;">
|
|
135
|
+
You are signing on behalf of <strong>{{signerKind}}:{{signerSlug}}</strong>.
|
|
136
|
+
</p>
|
|
137
|
+
<form action="{{signUrl}}" method="POST">
|
|
138
|
+
<input type="hidden" name="nonce" value="{{nonce}}" />
|
|
139
|
+
<input type="hidden" name="artifactPath" value="{{agreementPath}}" />
|
|
140
|
+
<input type="hidden" name="documentHash" value="{{documentHash}}" />
|
|
141
|
+
<input type="hidden" name="signer" value="{{signerKind}}:{{signerSlug}}" />
|
|
142
|
+
<input type="hidden" name="signerKind" value="{{signerKind}}" />
|
|
143
|
+
{{#signerEmail}}<input type="hidden" name="signerEmail" value="{{signerEmail}}" />{{/signerEmail}}
|
|
144
|
+
|
|
145
|
+
<div class="field">
|
|
146
|
+
<label for="signerName">Type your full legal name to sign</label>
|
|
147
|
+
<input id="signerName" name="signerName" type="text" required value="{{signerName}}" autocomplete="name" />
|
|
148
|
+
</div>
|
|
149
|
+
<button type="submit">Sign agreement</button>
|
|
150
|
+
</form>
|
|
151
|
+
</section>
|
|
152
|
+
|
|
153
|
+
<div class="meta">
|
|
154
|
+
<dl>
|
|
155
|
+
<dt>Document</dt><dd>{{agreementPath}}</dd>
|
|
156
|
+
<dt>SHA-256</dt><dd>{{documentHash}}</dd>
|
|
157
|
+
</dl>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<p class="legal">
|
|
161
|
+
Typing your name and clicking <em>Sign agreement</em> creates a binding electronic signature on this version of the document. Your typed name, IP, browser user agent, and the document SHA-256 hash above are recorded as evidence. If the document changes, this signature does not apply to the new version.
|
|
162
|
+
</p>
|
|
163
|
+
</body>
|
|
164
|
+
</html>
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
---
|
|
2
|
+
template: true
|
|
3
|
+
name: agency.deliverable-review
|
|
4
|
+
version: 1.0.0-alpha
|
|
5
|
+
description: |
|
|
6
|
+
Counterparty validation page for a DELIVERABLE.md. Shows the work product
|
|
7
|
+
(description + attachments + screenshot previews), then offers two paths:
|
|
8
|
+
approve (typed-name signature → workflow advances) or request revisions
|
|
9
|
+
(writes a feedback comment + advances workflow back to `revise`).
|
|
10
|
+
|
|
11
|
+
Connection to data:
|
|
12
|
+
- Reads DELIVERABLE.md frontmatter for status, requiredSignatures, attachments[].
|
|
13
|
+
- The host computes documentHash over deliverable bytes + attachment refs.
|
|
14
|
+
- Approval POSTs to /sign — same endpoint as agreement / invoice signing.
|
|
15
|
+
- Revision request POSTs to /revise — host appends a comment + flips status.
|
|
16
|
+
author: agencies.sh
|
|
17
|
+
variables:
|
|
18
|
+
agencyName: "Agency"
|
|
19
|
+
deliverableTitle: "Deliverable"
|
|
20
|
+
deliverablePath: "engagements/example/deliverables/v1/DELIVERABLE.md"
|
|
21
|
+
deliverableBodyHtml: ""
|
|
22
|
+
documentHash: "0000000000000000000000000000000000000000000000000000000000000000"
|
|
23
|
+
signerKind: "counterparty"
|
|
24
|
+
signerSlug: "example-corp"
|
|
25
|
+
signerName: ""
|
|
26
|
+
signerEmail: ""
|
|
27
|
+
nonce: ""
|
|
28
|
+
signUrl: "/api/agency/sign-deliverable"
|
|
29
|
+
reviseUrl: "/api/agency/revise-deliverable"
|
|
30
|
+
attachments: []
|
|
31
|
+
context: ""
|
|
32
|
+
engagementName: ""
|
|
33
|
+
sources: {}
|
|
34
|
+
---
|
|
35
|
+
<!doctype html>
|
|
36
|
+
<html lang="en">
|
|
37
|
+
<head>
|
|
38
|
+
<meta charset="utf-8" />
|
|
39
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
40
|
+
<title>Review: {{deliverableTitle}}</title>
|
|
41
|
+
<style>
|
|
42
|
+
:root { --fg: #1a1a1a; --muted: #666; --bg: #faf9f7; --panel: #fff; --border: #e5e5e5; --accent: #1a1a1a; --ok: #0a7d30; --danger: #b91c1c; }
|
|
43
|
+
body { font: 15px/1.55 ui-sans-serif, system-ui, sans-serif; max-width: 44rem; margin: 0 auto; padding: 2.5rem 1.5rem; color: var(--fg); background: var(--bg); }
|
|
44
|
+
h1 { font-size: 1.5rem; letter-spacing: -0.01em; margin: 0 0 0.25rem; }
|
|
45
|
+
.lead { color: var(--muted); margin: 0 0 2rem; font-size: 0.9375rem; }
|
|
46
|
+
.panel { background: var(--panel); border: 1px solid var(--border); border-radius: 8px; padding: 1.5rem; margin-bottom: 1.25rem; }
|
|
47
|
+
.panel h2 { font-size: 0.8125rem; text-transform: uppercase; letter-spacing: 0.08em; color: #666; margin: 0 0 0.875rem; }
|
|
48
|
+
.body { font-size: 0.9375rem; line-height: 1.6; color: #222; }
|
|
49
|
+
.body :is(p, ul, ol, blockquote) { margin: 0 0 0.75rem; }
|
|
50
|
+
.attachments { display: grid; gap: 0.625rem; }
|
|
51
|
+
.attachment { display: flex; gap: 0.875rem; align-items: center; padding: 0.75rem 0.875rem; background: #f7f5ee; border: 1px solid #e9e5d8; border-radius: 6px; }
|
|
52
|
+
.attachment .icon { font-size: 1.25rem; line-height: 1; }
|
|
53
|
+
.attachment .meta { flex: 1; }
|
|
54
|
+
.attachment .name { font-weight: 500; font-size: 0.9375rem; }
|
|
55
|
+
.attachment .size { font-size: 0.8125rem; color: var(--muted); margin-top: 0.125rem; }
|
|
56
|
+
.attachment a { color: #2563eb; text-decoration: none; font-size: 0.875rem; }
|
|
57
|
+
.attachment a:hover { text-decoration: underline; }
|
|
58
|
+
.actions { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; }
|
|
59
|
+
.field { margin-bottom: 1rem; }
|
|
60
|
+
label { display: block; font-size: 0.875rem; font-weight: 500; margin-bottom: 0.375rem; }
|
|
61
|
+
input, textarea { width: 100%; padding: 0.625rem 0.75rem; border: 1px solid #d4d4d4; border-radius: 6px; font: inherit; box-sizing: border-box; background: #fff; }
|
|
62
|
+
input:focus, textarea:focus { outline: 2px solid #2563eb; outline-offset: -2px; }
|
|
63
|
+
textarea { min-height: 6rem; resize: vertical; line-height: 1.5; }
|
|
64
|
+
button { width: 100%; padding: 0.75rem 1rem; border: 0; border-radius: 6px; font: inherit; font-weight: 500; cursor: pointer; }
|
|
65
|
+
button.approve { background: var(--ok); color: #fff; }
|
|
66
|
+
button.approve:hover { background: #086324; }
|
|
67
|
+
button.revise { background: #fff; color: var(--fg); border: 1px solid var(--border); }
|
|
68
|
+
button.revise:hover { background: #f7f5ee; }
|
|
69
|
+
.meta-block { font-size: 0.8125rem; color: var(--muted); padding: 0.75rem 0.875rem; background: #f5f4f0; border: 1px solid var(--border); border-radius: 6px; }
|
|
70
|
+
.meta-block dl { margin: 0; display: grid; grid-template-columns: max-content 1fr; gap: 0.25rem 0.75rem; }
|
|
71
|
+
.meta-block dt { font-weight: 500; color: #444; }
|
|
72
|
+
.meta-block dd { margin: 0; font-family: ui-monospace, SF Mono, monospace; font-size: 0.75rem; word-break: break-all; }
|
|
73
|
+
details { margin-top: 1.25rem; }
|
|
74
|
+
summary { cursor: pointer; font-size: 0.875rem; color: var(--muted); }
|
|
75
|
+
summary:hover { color: var(--fg); }
|
|
76
|
+
</style>
|
|
77
|
+
</head>
|
|
78
|
+
<body>
|
|
79
|
+
<p class="lead">{{agencyName}}{{#engagementName}} · {{engagementName}}{{/engagementName}}</p>
|
|
80
|
+
<h1>Review & sign: {{deliverableTitle}}</h1>
|
|
81
|
+
|
|
82
|
+
{{#context}}<p style="margin:-1rem 0 1.5rem;color:var(--muted);">{{context}}</p>{{/context}}
|
|
83
|
+
|
|
84
|
+
{{#deliverableBodyHtml}}
|
|
85
|
+
<section class="panel body">
|
|
86
|
+
{{{deliverableBodyHtml}}}
|
|
87
|
+
</section>
|
|
88
|
+
{{/deliverableBodyHtml}}
|
|
89
|
+
|
|
90
|
+
{{#attachments.length}}
|
|
91
|
+
<section class="panel">
|
|
92
|
+
<h2>Attachments</h2>
|
|
93
|
+
<div class="attachments">
|
|
94
|
+
{{#attachments}}
|
|
95
|
+
<div class="attachment">
|
|
96
|
+
<span class="icon">{{icon}}</span>
|
|
97
|
+
<div class="meta">
|
|
98
|
+
<div class="name">{{name}}</div>
|
|
99
|
+
<div class="size">{{sizeFormatted}}{{#kind}} · {{kind}}{{/kind}}</div>
|
|
100
|
+
</div>
|
|
101
|
+
<a href="{{url}}" target="_blank" rel="noopener">View</a>
|
|
102
|
+
</div>
|
|
103
|
+
{{/attachments}}
|
|
104
|
+
</div>
|
|
105
|
+
</section>
|
|
106
|
+
{{/attachments.length}}
|
|
107
|
+
|
|
108
|
+
<section class="panel">
|
|
109
|
+
<h2>Decision</h2>
|
|
110
|
+
<div class="actions">
|
|
111
|
+
<form action="{{signUrl}}" method="POST">
|
|
112
|
+
<input type="hidden" name="nonce" value="{{nonce}}" />
|
|
113
|
+
<input type="hidden" name="artifactPath" value="{{deliverablePath}}" />
|
|
114
|
+
<input type="hidden" name="documentHash" value="{{documentHash}}" />
|
|
115
|
+
<input type="hidden" name="signer" value="{{signerKind}}:{{signerSlug}}" />
|
|
116
|
+
<input type="hidden" name="signerKind" value="{{signerKind}}" />
|
|
117
|
+
{{#signerEmail}}<input type="hidden" name="signerEmail" value="{{signerEmail}}" />{{/signerEmail}}
|
|
118
|
+
<div class="field">
|
|
119
|
+
<label for="approveName">Type your name to approve</label>
|
|
120
|
+
<input id="approveName" name="signerName" type="text" required value="{{signerName}}" autocomplete="name" />
|
|
121
|
+
</div>
|
|
122
|
+
<button type="submit" class="approve">Approve & sign</button>
|
|
123
|
+
</form>
|
|
124
|
+
|
|
125
|
+
<form action="{{reviseUrl}}" method="POST">
|
|
126
|
+
<input type="hidden" name="nonce" value="{{nonce}}" />
|
|
127
|
+
<input type="hidden" name="artifactPath" value="{{deliverablePath}}" />
|
|
128
|
+
<input type="hidden" name="signer" value="{{signerKind}}:{{signerSlug}}" />
|
|
129
|
+
<div class="field">
|
|
130
|
+
<label for="reviseFeedback">Request revisions (what should change?)</label>
|
|
131
|
+
<textarea id="reviseFeedback" name="feedback" required></textarea>
|
|
132
|
+
</div>
|
|
133
|
+
<button type="submit" class="revise">Request revisions</button>
|
|
134
|
+
</form>
|
|
135
|
+
</div>
|
|
136
|
+
</section>
|
|
137
|
+
|
|
138
|
+
<details>
|
|
139
|
+
<summary>Document details</summary>
|
|
140
|
+
<div class="meta-block" style="margin-top:0.625rem;">
|
|
141
|
+
<dl>
|
|
142
|
+
<dt>Path</dt><dd>{{deliverablePath}}</dd>
|
|
143
|
+
<dt>SHA-256</dt><dd>{{documentHash}}</dd>
|
|
144
|
+
</dl>
|
|
145
|
+
</div>
|
|
146
|
+
</details>
|
|
147
|
+
</body>
|
|
148
|
+
</html>
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
---
|
|
2
|
+
template: true
|
|
3
|
+
name: agency.engagement-dashboard
|
|
4
|
+
version: 1.0.0-alpha
|
|
5
|
+
description: |
|
|
6
|
+
Operator-facing live dashboard for a single engagement. Reads ENGAGEMENT.md +
|
|
7
|
+
AGREEMENT.md + the deliverables/ and invoices/ folders + the tail of
|
|
8
|
+
`audit-log.jsonl`. Re-renders every 30s — the workflow updates the underlying
|
|
9
|
+
workspace files, this template just reflects state.
|
|
10
|
+
|
|
11
|
+
Data flow:
|
|
12
|
+
1. Workflow tool transitions ENGAGEMENT.md frontmatter (status, activeStep).
|
|
13
|
+
2. signArtifactTool / recordDeliverableTool / issueInvoiceCheckoutTool write
|
|
14
|
+
artifacts under engagements/<slug>/.
|
|
15
|
+
3. recordAuditEvent appends a hash-chained line to audit/audit-log.jsonl.
|
|
16
|
+
4. This template re-reads everything on the next refresh tick.
|
|
17
|
+
|
|
18
|
+
No "update the dashboard" tool call is needed — the dashboard is a *view* of
|
|
19
|
+
workspace state, not a separate persistence layer.
|
|
20
|
+
author: agencies.sh
|
|
21
|
+
refreshEvery: 30s
|
|
22
|
+
variables:
|
|
23
|
+
engagementSlug: "2026-acme-website-redesign"
|
|
24
|
+
agencyName: "Acme Agency"
|
|
25
|
+
signUrlBase: "/g/acme/sign"
|
|
26
|
+
sources:
|
|
27
|
+
engagement:
|
|
28
|
+
kind: file
|
|
29
|
+
path: "engagements/{{engagementSlug}}/ENGAGEMENT.md"
|
|
30
|
+
agreement:
|
|
31
|
+
kind: file
|
|
32
|
+
path: "engagements/{{engagementSlug}}/AGREEMENT.md"
|
|
33
|
+
deliverables:
|
|
34
|
+
kind: query
|
|
35
|
+
include: "engagements/{{engagementSlug}}/deliverables/*/DELIVERABLE.md"
|
|
36
|
+
sort: "createdAt:asc"
|
|
37
|
+
fields: ["slug", "name", "status", "requiredSignatures", "createdAt"]
|
|
38
|
+
invoices:
|
|
39
|
+
kind: query
|
|
40
|
+
include: "engagements/{{engagementSlug}}/invoices/*/INVOICE.md"
|
|
41
|
+
sort: "issuedAt:desc"
|
|
42
|
+
fields: ["invoiceNumber", "amount", "currency", "status", "issuedAt", "paidAt"]
|
|
43
|
+
audit:
|
|
44
|
+
kind: tool
|
|
45
|
+
ref: governance.audit_tail
|
|
46
|
+
params:
|
|
47
|
+
logPath: "engagements/{{engagementSlug}}/audit/audit-log.jsonl"
|
|
48
|
+
lines: 12
|
|
49
|
+
---
|
|
50
|
+
<!doctype html>
|
|
51
|
+
<html lang="en">
|
|
52
|
+
<head>
|
|
53
|
+
<meta charset="utf-8" />
|
|
54
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
55
|
+
<title>{{engagement.name}} · Engagement</title>
|
|
56
|
+
<style>
|
|
57
|
+
:root {
|
|
58
|
+
--fg: #1a1a1a;
|
|
59
|
+
--muted: #666;
|
|
60
|
+
--bg: #faf9f7;
|
|
61
|
+
--panel: #ffffff;
|
|
62
|
+
--border: #e5e5e5;
|
|
63
|
+
--accent: #1a1a1a;
|
|
64
|
+
--ok: #0a7d30;
|
|
65
|
+
--warn: #b45309;
|
|
66
|
+
--danger: #b91c1c;
|
|
67
|
+
}
|
|
68
|
+
body { font: 15px/1.55 ui-sans-serif, system-ui, sans-serif; max-width: 64rem; margin: 0 auto; padding: 2.5rem 1.5rem; color: var(--fg); background: var(--bg); }
|
|
69
|
+
header { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 2rem; }
|
|
70
|
+
h1 { font-size: 1.625rem; letter-spacing: -0.01em; margin: 0 0 0.25rem; }
|
|
71
|
+
.lead { color: var(--muted); margin: 0; font-size: 0.9375rem; }
|
|
72
|
+
.panel { background: var(--panel); border: 1px solid var(--border); border-radius: 8px; padding: 1.25rem 1.5rem; margin-bottom: 1.25rem; }
|
|
73
|
+
.panel h2 { font-size: 0.8125rem; text-transform: uppercase; letter-spacing: 0.08em; color: #666; margin: 0 0 1rem; }
|
|
74
|
+
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; }
|
|
75
|
+
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.25rem; }
|
|
76
|
+
.stat .label { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em; color: #666; }
|
|
77
|
+
.stat .value { font-size: 1.75rem; font-weight: 600; letter-spacing: -0.02em; margin-top: 0.25rem; }
|
|
78
|
+
.pill { display: inline-block; padding: 0.125rem 0.5rem; border-radius: 999px; font-size: 0.75rem; font-weight: 500; line-height: 1.5; vertical-align: middle; }
|
|
79
|
+
.pill.ok { background: #e7f5eb; color: var(--ok); }
|
|
80
|
+
.pill.warn { background: #fef3c7; color: var(--warn); }
|
|
81
|
+
.pill.danger { background: #fee2e2; color: var(--danger); }
|
|
82
|
+
.pill.muted { background: #f0eee9; color: #555; }
|
|
83
|
+
table { width: 100%; border-collapse: collapse; }
|
|
84
|
+
th, td { text-align: left; padding: 0.625rem 0.75rem; border-bottom: 1px solid #efece5; vertical-align: top; }
|
|
85
|
+
th { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em; color: #777; font-weight: 500; }
|
|
86
|
+
td.num { text-align: right; font-variant-numeric: tabular-nums; }
|
|
87
|
+
td.mono { font-family: ui-monospace, SF Mono, monospace; font-size: 0.8125rem; color: #444; }
|
|
88
|
+
.audit { font-family: ui-monospace, SF Mono, monospace; font-size: 0.8125rem; color: #555; line-height: 1.7; }
|
|
89
|
+
.audit .row { display: grid; grid-template-columns: 9rem 7rem 1fr; gap: 0.75rem; padding: 0.25rem 0; border-bottom: 1px dashed #f0eee5; }
|
|
90
|
+
.audit .row:last-child { border-bottom: 0; }
|
|
91
|
+
.audit time { color: #888; }
|
|
92
|
+
.empty { padding: 1.25rem; text-align: center; color: var(--muted); font-size: 0.875rem; background: #f7f5f0; border-radius: 6px; }
|
|
93
|
+
a { color: #2563eb; text-decoration: none; }
|
|
94
|
+
a:hover { text-decoration: underline; }
|
|
95
|
+
</style>
|
|
96
|
+
</head>
|
|
97
|
+
<body>
|
|
98
|
+
<header>
|
|
99
|
+
<div>
|
|
100
|
+
<p class="lead">{{agencyName}} · engagement</p>
|
|
101
|
+
<h1>{{engagement.name}}</h1>
|
|
102
|
+
</div>
|
|
103
|
+
<div>
|
|
104
|
+
<span class="pill {{engagement.statusPillClass}}">{{engagement.status}}</span>
|
|
105
|
+
</div>
|
|
106
|
+
</header>
|
|
107
|
+
|
|
108
|
+
<div class="grid-3">
|
|
109
|
+
<div class="panel stat">
|
|
110
|
+
<p class="label">Counterparty</p>
|
|
111
|
+
<p class="value">{{engagement.counterpartyDisplayName}}</p>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="panel stat">
|
|
114
|
+
<p class="label">Active step</p>
|
|
115
|
+
<p class="value">{{engagement.activeStep}}</p>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="panel stat">
|
|
118
|
+
<p class="label">Procedure</p>
|
|
119
|
+
<p class="value">{{engagement.activeProcedure}}</p>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<section class="panel">
|
|
124
|
+
<h2>Agreement</h2>
|
|
125
|
+
<table>
|
|
126
|
+
<tbody>
|
|
127
|
+
<tr><th>Status</th><td><span class="pill {{agreement.statusPillClass}}">{{agreement.status}}</span></td></tr>
|
|
128
|
+
<tr><th>Signed by</th><td>{{agreement.signedByLabel}}</td></tr>
|
|
129
|
+
<tr><th>Document hash</th><td class="mono">{{agreement.documentHash}}</td></tr>
|
|
130
|
+
</tbody>
|
|
131
|
+
</table>
|
|
132
|
+
</section>
|
|
133
|
+
|
|
134
|
+
<section class="panel">
|
|
135
|
+
<h2>Deliverables</h2>
|
|
136
|
+
{{#deliverables.length}}
|
|
137
|
+
<table>
|
|
138
|
+
<thead><tr><th>Name</th><th>Status</th><th>Pending signatures</th><th>Created</th></tr></thead>
|
|
139
|
+
<tbody>
|
|
140
|
+
{{#deliverables}}
|
|
141
|
+
<tr>
|
|
142
|
+
<td>{{name}}</td>
|
|
143
|
+
<td><span class="pill {{statusPillClass}}">{{status}}</span></td>
|
|
144
|
+
<td>{{pendingSignatureCount}}</td>
|
|
145
|
+
<td class="mono">{{createdAt}}</td>
|
|
146
|
+
</tr>
|
|
147
|
+
{{/deliverables}}
|
|
148
|
+
</tbody>
|
|
149
|
+
</table>
|
|
150
|
+
{{/deliverables.length}}
|
|
151
|
+
{{^deliverables.length}}<div class="empty">No deliverables yet — workflow has not advanced to <code>execute</code>.</div>{{/deliverables.length}}
|
|
152
|
+
</section>
|
|
153
|
+
|
|
154
|
+
<section class="panel">
|
|
155
|
+
<h2>Invoices</h2>
|
|
156
|
+
{{#invoices.length}}
|
|
157
|
+
<table>
|
|
158
|
+
<thead><tr><th>Invoice #</th><th>Amount</th><th>Status</th><th>Issued</th><th>Paid</th></tr></thead>
|
|
159
|
+
<tbody>
|
|
160
|
+
{{#invoices}}
|
|
161
|
+
<tr>
|
|
162
|
+
<td class="mono">{{invoiceNumber}}</td>
|
|
163
|
+
<td class="num">{{amountFormatted}}</td>
|
|
164
|
+
<td><span class="pill {{statusPillClass}}">{{status}}</span></td>
|
|
165
|
+
<td class="mono">{{issuedAt}}</td>
|
|
166
|
+
<td class="mono">{{paidAt}}</td>
|
|
167
|
+
</tr>
|
|
168
|
+
{{/invoices}}
|
|
169
|
+
</tbody>
|
|
170
|
+
</table>
|
|
171
|
+
{{/invoices.length}}
|
|
172
|
+
{{^invoices.length}}<div class="empty">No invoices issued.</div>{{/invoices.length}}
|
|
173
|
+
</section>
|
|
174
|
+
|
|
175
|
+
<section class="panel">
|
|
176
|
+
<h2>Recent audit events</h2>
|
|
177
|
+
{{#audit.length}}
|
|
178
|
+
<div class="audit">
|
|
179
|
+
{{#audit}}
|
|
180
|
+
<div class="row">
|
|
181
|
+
<time>{{createdAt}}</time>
|
|
182
|
+
<span>{{action}}</span>
|
|
183
|
+
<span>{{actor}} → {{entityId}}</span>
|
|
184
|
+
</div>
|
|
185
|
+
{{/audit}}
|
|
186
|
+
</div>
|
|
187
|
+
{{/audit.length}}
|
|
188
|
+
{{^audit.length}}<div class="empty">Audit log is empty.</div>{{/audit.length}}
|
|
189
|
+
</section>
|
|
190
|
+
</body>
|
|
191
|
+
</html>
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
template: true
|
|
3
|
+
name: agency.invoice-pdf
|
|
4
|
+
version: 1.0.0-alpha
|
|
5
|
+
description: |
|
|
6
|
+
Print-targeted invoice render. The host application reads INVOICE.md +
|
|
7
|
+
ENGAGEMENT.md + AGENCY.md (or COMPANY.md), passes the merged variables in,
|
|
8
|
+
and either inlines this HTML in an email or feeds it through a PDF engine
|
|
9
|
+
(Chromium / wkhtmltopdf) to archive a frozen PDF alongside the .md.
|
|
10
|
+
|
|
11
|
+
Document integrity: the issuing tool computes SHA-256 of the rendered HTML
|
|
12
|
+
*before* PDF conversion and stores it in INVOICE.md frontmatter as
|
|
13
|
+
`documentHash`. A signature.json on the invoice references the same hash —
|
|
14
|
+
changing the rendered HTML invalidates the signature.
|
|
15
|
+
author: agencies.sh
|
|
16
|
+
variables:
|
|
17
|
+
agencyName: "Acme Agency"
|
|
18
|
+
agencyAddressHtml: ""
|
|
19
|
+
agencyTaxId: ""
|
|
20
|
+
invoiceNumber: "INV-2026-0001"
|
|
21
|
+
issuedAt: "2026-04-26"
|
|
22
|
+
dueAt: "2026-05-26"
|
|
23
|
+
status: "issued"
|
|
24
|
+
counterpartyDisplayName: "Counterparty"
|
|
25
|
+
counterpartyAddressHtml: ""
|
|
26
|
+
counterpartyTaxId: ""
|
|
27
|
+
currency: "EUR"
|
|
28
|
+
subtotalFormatted: "0.00"
|
|
29
|
+
taxLines: []
|
|
30
|
+
totalFormatted: "0.00"
|
|
31
|
+
amountDueFormatted: "0.00"
|
|
32
|
+
paymentTermsHtml: ""
|
|
33
|
+
notesHtml: ""
|
|
34
|
+
paymentLinkUrl: ""
|
|
35
|
+
lineItems: []
|
|
36
|
+
sources: {}
|
|
37
|
+
---
|
|
38
|
+
<!doctype html>
|
|
39
|
+
<html lang="en">
|
|
40
|
+
<head>
|
|
41
|
+
<meta charset="utf-8" />
|
|
42
|
+
<title>Invoice {{invoiceNumber}}</title>
|
|
43
|
+
<style>
|
|
44
|
+
@page { size: A4; margin: 18mm; }
|
|
45
|
+
:root {
|
|
46
|
+
--fg: #111;
|
|
47
|
+
--muted: #555;
|
|
48
|
+
--border: #d4d4d4;
|
|
49
|
+
--accent: #1a1a1a;
|
|
50
|
+
}
|
|
51
|
+
body { font: 11pt/1.5 ui-sans-serif, system-ui, sans-serif; color: var(--fg); margin: 0; padding: 0; }
|
|
52
|
+
.doc { max-width: 720px; margin: 0 auto; padding: 0; }
|
|
53
|
+
header { display: flex; justify-content: space-between; align-items: flex-start; padding-bottom: 1.5rem; border-bottom: 2px solid var(--accent); margin-bottom: 2rem; }
|
|
54
|
+
.agency { max-width: 60%; }
|
|
55
|
+
.agency h1 { font-size: 1.375rem; margin: 0 0 0.25rem; letter-spacing: -0.01em; }
|
|
56
|
+
.agency .tax { color: var(--muted); font-size: 0.875rem; }
|
|
57
|
+
.doc-title { text-align: right; }
|
|
58
|
+
.doc-title h2 { font-size: 1.125rem; text-transform: uppercase; letter-spacing: 0.1em; margin: 0 0 0.25rem; color: var(--muted); font-weight: 500; }
|
|
59
|
+
.doc-title .num { font-size: 1.375rem; font-weight: 600; font-family: ui-monospace, SF Mono, monospace; }
|
|
60
|
+
.meta { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.25rem; margin-bottom: 2rem; }
|
|
61
|
+
.meta h3 { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); margin: 0 0 0.375rem; font-weight: 600; }
|
|
62
|
+
.meta p { margin: 0; line-height: 1.45; }
|
|
63
|
+
.dates { display: flex; gap: 2rem; padding: 0.75rem 1rem; background: #f7f5ee; border-radius: 4px; margin-bottom: 2rem; font-size: 0.9375rem; }
|
|
64
|
+
.dates dt { font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); margin-bottom: 0.125rem; }
|
|
65
|
+
.dates dd { margin: 0; font-weight: 500; }
|
|
66
|
+
table.lines { width: 100%; border-collapse: collapse; margin-bottom: 1.5rem; }
|
|
67
|
+
table.lines th { text-align: left; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); padding: 0.5rem 0.75rem; border-bottom: 2px solid var(--accent); font-weight: 600; }
|
|
68
|
+
table.lines td { padding: 0.625rem 0.75rem; border-bottom: 1px solid #efefef; vertical-align: top; }
|
|
69
|
+
table.lines td.num { text-align: right; font-variant-numeric: tabular-nums; }
|
|
70
|
+
.totals { margin-left: auto; width: 18rem; font-size: 0.9375rem; }
|
|
71
|
+
.totals .row { display: flex; justify-content: space-between; padding: 0.375rem 0; }
|
|
72
|
+
.totals .row.grand { border-top: 2px solid var(--accent); margin-top: 0.5rem; padding-top: 0.625rem; font-weight: 600; font-size: 1.0625rem; }
|
|
73
|
+
.totals .row.due { color: var(--accent); margin-top: 0.5rem; padding: 0.625rem 0.75rem; background: #f7f5ee; border-radius: 4px; font-weight: 600; }
|
|
74
|
+
footer { margin-top: 2.5rem; padding-top: 1.25rem; border-top: 1px solid var(--border); font-size: 0.875rem; color: var(--muted); line-height: 1.55; }
|
|
75
|
+
.pay-link { display: inline-block; margin-top: 0.75rem; padding: 0.5rem 1rem; background: var(--accent); color: #fff; text-decoration: none; border-radius: 4px; font-weight: 500; font-size: 0.875rem; }
|
|
76
|
+
.pill { display: inline-block; padding: 0.125rem 0.625rem; border-radius: 999px; font-size: 0.75rem; font-weight: 500; background: #f0eee5; color: #555; vertical-align: middle; margin-left: 0.5rem; }
|
|
77
|
+
</style>
|
|
78
|
+
</head>
|
|
79
|
+
<body>
|
|
80
|
+
<div class="doc">
|
|
81
|
+
<header>
|
|
82
|
+
<div class="agency">
|
|
83
|
+
<h1>{{agencyName}}</h1>
|
|
84
|
+
{{{agencyAddressHtml}}}
|
|
85
|
+
{{#agencyTaxId}}<p class="tax">Tax ID: {{agencyTaxId}}</p>{{/agencyTaxId}}
|
|
86
|
+
</div>
|
|
87
|
+
<div class="doc-title">
|
|
88
|
+
<h2>Invoice <span class="pill">{{status}}</span></h2>
|
|
89
|
+
<div class="num">{{invoiceNumber}}</div>
|
|
90
|
+
</div>
|
|
91
|
+
</header>
|
|
92
|
+
|
|
93
|
+
<section class="meta">
|
|
94
|
+
<div>
|
|
95
|
+
<h3>Bill to</h3>
|
|
96
|
+
<p><strong>{{counterpartyDisplayName}}</strong></p>
|
|
97
|
+
{{{counterpartyAddressHtml}}}
|
|
98
|
+
{{#counterpartyTaxId}}<p class="tax">Tax ID: {{counterpartyTaxId}}</p>{{/counterpartyTaxId}}
|
|
99
|
+
</div>
|
|
100
|
+
<div class="dates">
|
|
101
|
+
<div><dt>Issued</dt><dd>{{issuedAt}}</dd></div>
|
|
102
|
+
<div><dt>Due</dt><dd>{{dueAt}}</dd></div>
|
|
103
|
+
</div>
|
|
104
|
+
</section>
|
|
105
|
+
|
|
106
|
+
<table class="lines">
|
|
107
|
+
<thead>
|
|
108
|
+
<tr><th>Description</th><th class="num">Qty</th><th class="num">Unit</th><th class="num">Total</th></tr>
|
|
109
|
+
</thead>
|
|
110
|
+
<tbody>
|
|
111
|
+
{{#lineItems}}
|
|
112
|
+
<tr>
|
|
113
|
+
<td>{{description}}</td>
|
|
114
|
+
<td class="num">{{quantity}}</td>
|
|
115
|
+
<td class="num">{{unitPriceFormatted}}</td>
|
|
116
|
+
<td class="num">{{totalFormatted}}</td>
|
|
117
|
+
</tr>
|
|
118
|
+
{{/lineItems}}
|
|
119
|
+
</tbody>
|
|
120
|
+
</table>
|
|
121
|
+
|
|
122
|
+
<div class="totals">
|
|
123
|
+
<div class="row"><span>Subtotal</span><span>{{subtotalFormatted}} {{currency}}</span></div>
|
|
124
|
+
{{#taxLines}}
|
|
125
|
+
<div class="row"><span>{{label}}</span><span>{{amountFormatted}} {{currency}}</span></div>
|
|
126
|
+
{{/taxLines}}
|
|
127
|
+
<div class="row grand"><span>Total</span><span>{{totalFormatted}} {{currency}}</span></div>
|
|
128
|
+
<div class="row due"><span>Amount due</span><span>{{amountDueFormatted}} {{currency}}</span></div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
{{#paymentLinkUrl}}
|
|
132
|
+
<p style="text-align:right;margin-top:1.25rem;"><a class="pay-link" href="{{paymentLinkUrl}}">Pay online</a></p>
|
|
133
|
+
{{/paymentLinkUrl}}
|
|
134
|
+
|
|
135
|
+
<footer>
|
|
136
|
+
{{#paymentTermsHtml}}<div>{{{paymentTermsHtml}}}</div>{{/paymentTermsHtml}}
|
|
137
|
+
{{#notesHtml}}<div style="margin-top:0.75rem;">{{{notesHtml}}}</div>{{/notesHtml}}
|
|
138
|
+
<p style="margin-top:1rem;font-size:0.75rem;">Generated by {{agencyName}} · agentagencies/v1</p>
|
|
139
|
+
</footer>
|
|
140
|
+
</div>
|
|
141
|
+
</body>
|
|
142
|
+
</html>
|