@superjackfruitlabs/salaam 0.0.3 → 0.0.4
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/CHANGELOG.md +45 -0
- package/README.md +12 -11
- package/dist/src/acp-outcome.js +177 -17
- package/dist/src/acp.js +41 -7
- package/dist/src/agent-profiles.js +45 -0
- package/dist/src/agentpod-authority.js +169 -11
- package/dist/src/agentpod-default-config.js +12 -0
- package/dist/src/agentpod-work-decision-protocol.js +40 -6
- package/dist/src/agentpod-work-decisions.js +108 -4
- package/dist/src/agentpod-work.js +1214 -127
- package/dist/src/board-reads.js +106 -0
- package/dist/src/children.js +83 -0
- package/dist/src/cli-help.js +27 -2
- package/dist/src/cli.js +87 -3
- package/dist/src/command-network.js +200 -0
- package/dist/src/command-sandbox.js +217 -0
- package/dist/src/execution-evidence.js +33 -1
- package/dist/src/host-bind.js +111 -0
- package/dist/src/host-breaker.js +88 -0
- package/dist/src/host-start.js +41 -1
- package/dist/src/host.js +192 -6
- package/dist/src/house-extension.js +162 -57
- package/dist/src/local-coding.js +214 -18
- package/dist/src/local-edit-helper.js +25 -0
- package/dist/src/local-undo.js +25 -16
- package/dist/src/managed.js +1 -1
- package/dist/src/operator-mcp-cli.js +107 -0
- package/dist/src/operator-mcp.js +342 -0
- package/dist/src/persona.js +31 -0
- package/dist/src/pi.js +22 -1
- package/dist/src/protocol.js +5 -2
- package/dist/src/rate-limit-retries.js +62 -0
- package/dist/src/recurring-work.js +1 -0
- package/dist/src/repository-snapshots.js +5 -2
- package/dist/src/run-grants.js +62 -0
- package/dist/src/service.js +141 -6
- package/dist/src/station-checkpoints.js +139 -0
- package/dist/src/station-config.js +8 -1
- package/dist/src/station-files.js +20 -1
- package/dist/src/station-task-verification.js +6 -3
- package/dist/src/station-work.js +315 -22
- package/dist/src/store.js +19 -0
- package/dist/src/supervisor.js +177 -5
- package/dist/src/task-budget.js +39 -3
- package/dist/src/task-critic.js +115 -0
- package/dist/src/task-driver.js +46 -2
- package/dist/src/task-policy.js +44 -12
- package/dist/src/task-verification.js +42 -9
- package/dist/src/task-work.js +47 -0
- package/dist/src/tool-identity.js +69 -0
- package/dist/src/usage.js +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/src/acp-outcome.ts +169 -19
- package/src/acp.ts +53 -7
- package/src/agent-profiles.ts +48 -0
- package/src/agentpod-authority.ts +234 -15
- package/src/agentpod-default-config.ts +16 -0
- package/src/agentpod-work-decision-protocol.ts +69 -8
- package/src/agentpod-work-decisions.ts +123 -5
- package/src/agentpod-work.ts +1455 -152
- package/src/board-reads.ts +148 -0
- package/src/children.ts +112 -0
- package/src/cli-help.ts +27 -2
- package/src/cli.ts +109 -3
- package/src/command-network.ts +215 -0
- package/src/command-sandbox.ts +273 -0
- package/src/execution-evidence.ts +31 -1
- package/src/host-bind.ts +156 -0
- package/src/host-breaker.ts +89 -0
- package/src/host-start.ts +51 -1
- package/src/host.ts +244 -6
- package/src/house-extension.ts +174 -56
- package/src/local-coding.ts +270 -18
- package/src/local-edit-helper.ts +26 -0
- package/src/local-undo.ts +29 -14
- package/src/managed.ts +2 -2
- package/src/operator-mcp-cli.ts +108 -0
- package/src/operator-mcp.ts +420 -0
- package/src/persona.ts +37 -0
- package/src/pi.ts +23 -1
- package/src/protocol.ts +6 -1
- package/src/rate-limit-retries.ts +69 -0
- package/src/recurring-work.ts +1 -0
- package/src/repository-snapshots.ts +5 -2
- package/src/run-grants.ts +107 -0
- package/src/service.ts +222 -6
- package/src/station-checkpoints.ts +183 -0
- package/src/station-config.ts +10 -1
- package/src/station-files.ts +20 -1
- package/src/station-task-verification.ts +6 -3
- package/src/station-work.ts +369 -20
- package/src/store.ts +18 -0
- package/src/supervisor.ts +203 -4
- package/src/task-budget.ts +48 -3
- package/src/task-critic.ts +171 -0
- package/src/task-driver.ts +49 -2
- package/src/task-policy.ts +65 -13
- package/src/task-verification.ts +53 -9
- package/src/task-work.ts +75 -0
- package/src/tool-identity.ts +110 -0
- package/src/usage.ts +1 -1
|
@@ -2,14 +2,43 @@ import { randomUUID } from 'node:crypto';
|
|
|
2
2
|
import type { AgentPodWork } from './agentpod-work.js';
|
|
3
3
|
import type { ManagedWork } from './managed.js';
|
|
4
4
|
import { canonical, digest, ServiceError } from './protocol.js';
|
|
5
|
-
import {
|
|
5
|
+
import { type RunGrants, runGrantCovers, runGrantPrefix } from './run-grants.js';
|
|
6
|
+
import {
|
|
7
|
+
workDecisionAnswer,
|
|
8
|
+
type WorkDecision,
|
|
9
|
+
type WorkDecisionDetail,
|
|
10
|
+
} from './agentpod-work-decision-protocol.js';
|
|
6
11
|
/** Private authenticated Hub control only. Answers choose an existing approval;
|
|
7
12
|
* they never execute an operation or supply a second effect grant. */
|
|
8
13
|
export class AgentPodWorkDecisions {
|
|
9
14
|
constructor(
|
|
10
15
|
readonly work: AgentPodWork,
|
|
11
16
|
readonly managed: ManagedWork,
|
|
17
|
+
readonly grants?: RunGrants,
|
|
12
18
|
) {}
|
|
19
|
+
/** The run directory a grant for this operation would cover, when it is a covered write. */
|
|
20
|
+
private grantable(binding: unknown, op: { kind: string; value: string }): string | null {
|
|
21
|
+
if (!this.grants || op.kind !== 'workspace') return null;
|
|
22
|
+
const run = (binding as { run?: { id?: unknown } } | null)?.run;
|
|
23
|
+
const prefix = runGrantPrefix(typeof run?.id === 'string' ? run.id : '');
|
|
24
|
+
let args: Record<string, unknown> = {};
|
|
25
|
+
try {
|
|
26
|
+
args = JSON.parse(op.value) as Record<string, unknown>;
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return prefix && runGrantCovers(prefix, args) ? prefix : null;
|
|
31
|
+
}
|
|
32
|
+
/** "Always allow" covers exactly what the standing workspace-write rule answers: a workspace
|
|
33
|
+
* write anywhere in the workspace, never a delete or a hidden path. */
|
|
34
|
+
private alwaysAllowable(op: { kind: string; value: string }): boolean {
|
|
35
|
+
if (op.kind !== 'workspace') return false;
|
|
36
|
+
try {
|
|
37
|
+
return runGrantCovers('', JSON.parse(op.value) as Record<string, unknown>);
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
13
42
|
private owner(binding: unknown) {
|
|
14
43
|
const root = this.work.authority.inspect(binding);
|
|
15
44
|
const row = this.work.store.db
|
|
@@ -51,10 +80,23 @@ export class AgentPodWorkDecisions {
|
|
|
51
80
|
version: 1,
|
|
52
81
|
request_id: randomUUID(),
|
|
53
82
|
...identity,
|
|
54
|
-
question:
|
|
83
|
+
question: approvalDetail(op).summary + '?',
|
|
84
|
+
detail: approvalDetail(op),
|
|
55
85
|
options: [
|
|
56
|
-
{ id: 'approve', title: 'Approve once' },
|
|
57
|
-
|
|
86
|
+
{ id: 'approve' as const, title: 'Approve once' },
|
|
87
|
+
...(this.grantable(root.binding, op)
|
|
88
|
+
? [{ id: 'approve-run' as const, title: 'Approve for this run' }]
|
|
89
|
+
: []),
|
|
90
|
+
...(this.alwaysAllowable(op)
|
|
91
|
+
? [
|
|
92
|
+
{
|
|
93
|
+
id: 'always' as const,
|
|
94
|
+
title: 'Always allow this agent to write in its workspace',
|
|
95
|
+
requires: 'workspace-admin' as const,
|
|
96
|
+
},
|
|
97
|
+
]
|
|
98
|
+
: []),
|
|
99
|
+
{ id: 'deny' as const, title: 'Deny' },
|
|
58
100
|
],
|
|
59
101
|
}),
|
|
60
102
|
);
|
|
@@ -97,13 +139,45 @@ export class AgentPodWorkDecisions {
|
|
|
97
139
|
approval.expires_at !== offered.expires_at
|
|
98
140
|
)
|
|
99
141
|
throw new ServiceError('approval_mismatch', 'Approval ownership changed');
|
|
142
|
+
// "Approve for this run" must have been offered, and still be grantable, to be chosen.
|
|
143
|
+
const prefix =
|
|
144
|
+
answer.option_id === 'approve-run'
|
|
145
|
+
? this.grantable(root.binding, this.managed.operation(offered.operation_id))
|
|
146
|
+
: null;
|
|
147
|
+
if (
|
|
148
|
+
answer.option_id === 'approve-run' &&
|
|
149
|
+
(!prefix || !offered.options.some((o) => o.id === 'approve-run'))
|
|
150
|
+
)
|
|
151
|
+
throw new ServiceError('approval_mismatch', 'This approval cannot cover the run');
|
|
152
|
+
// "Always allow" approves this write; the standing rule is the hub's, set before it forwards.
|
|
153
|
+
if (
|
|
154
|
+
answer.option_id === 'always' &&
|
|
155
|
+
(!offered.options.some((o) => o.id === 'always') ||
|
|
156
|
+
!this.alwaysAllowable(this.managed.operation(offered.operation_id)))
|
|
157
|
+
)
|
|
158
|
+
throw new ServiceError(
|
|
159
|
+
'approval_mismatch',
|
|
160
|
+
'Always allow was not offered for this write',
|
|
161
|
+
);
|
|
100
162
|
this.managed.decide(
|
|
101
163
|
offered.approval_id,
|
|
102
164
|
answer.request_id,
|
|
103
165
|
offered.approval_revision,
|
|
104
166
|
offered.operation_digest,
|
|
105
|
-
answer.option_id,
|
|
167
|
+
answer.option_id === 'deny' ? 'deny' : 'approve',
|
|
106
168
|
);
|
|
169
|
+
const grant =
|
|
170
|
+
answer.option_id === 'approve-run'
|
|
171
|
+
? this.grants!.createInTransaction({
|
|
172
|
+
session_id: root.session_id,
|
|
173
|
+
task_id: task.id,
|
|
174
|
+
plan_revision: offered.plan_revision,
|
|
175
|
+
binding_digest: offered.binding_digest,
|
|
176
|
+
path_prefix: prefix!,
|
|
177
|
+
approval_id: offered.approval_id,
|
|
178
|
+
granted_by: answer.answered_by,
|
|
179
|
+
})
|
|
180
|
+
: null;
|
|
107
181
|
this.work.store.execution.assert(root.session_id);
|
|
108
182
|
return {
|
|
109
183
|
version: 1 as const,
|
|
@@ -111,8 +185,52 @@ export class AgentPodWorkDecisions {
|
|
|
111
185
|
decision_digest: answer.decision_digest,
|
|
112
186
|
approval_id: offered.approval_id,
|
|
113
187
|
decision: answer.option_id,
|
|
188
|
+
...(grant ? { grant_id: grant.id } : {}),
|
|
114
189
|
};
|
|
115
190
|
},
|
|
116
191
|
);
|
|
117
192
|
}
|
|
118
193
|
}
|
|
194
|
+
|
|
195
|
+
/** Readable approvals: what a person approves, from the operation's own canonical arguments only.
|
|
196
|
+
* A workspace write names its file, the change, its size, a short preview and the agent's one-line
|
|
197
|
+
* reason; anything else names its kind and target. The digest still binds the exact operation. */
|
|
198
|
+
export function approvalDetail(op: {
|
|
199
|
+
kind: string;
|
|
200
|
+
action_key: string;
|
|
201
|
+
target: string;
|
|
202
|
+
value: string;
|
|
203
|
+
operation_digest: string;
|
|
204
|
+
}): WorkDecisionDetail {
|
|
205
|
+
const base = { version: 1 as const, reason: null, files: [], digest: op.operation_digest };
|
|
206
|
+
if (op.kind === 'review') return { ...base, summary: 'Accept this step for review' };
|
|
207
|
+
if (op.kind !== 'workspace')
|
|
208
|
+
return { ...base, summary: ('Run ' + op.kind + ' ' + op.target).slice(0, 200) };
|
|
209
|
+
let args: Record<string, unknown> = {};
|
|
210
|
+
try {
|
|
211
|
+
args = JSON.parse(op.value) as Record<string, unknown>;
|
|
212
|
+
} catch {
|
|
213
|
+
/* not a patch: the generic form below */
|
|
214
|
+
}
|
|
215
|
+
const path = typeof args.path === 'string' ? args.path : op.target.replace(/^station:/, '');
|
|
216
|
+
const content = typeof args.content === 'string' ? args.content : null;
|
|
217
|
+
const change = args.content === null ? 'delete' : args.expected_hash === null ? 'create' : 'edit';
|
|
218
|
+
const verb = { create: 'Create', edit: 'Edit', delete: 'Delete' }[change];
|
|
219
|
+
const reason =
|
|
220
|
+
typeof args.reason === 'string' && args.reason.trim()
|
|
221
|
+
? args.reason.replace(/\s+/g, ' ').trim().slice(0, 300)
|
|
222
|
+
: null;
|
|
223
|
+
return {
|
|
224
|
+
...base,
|
|
225
|
+
summary: (verb + ' ' + path + ' in the workspace').slice(0, 200),
|
|
226
|
+
reason,
|
|
227
|
+
files: [
|
|
228
|
+
{
|
|
229
|
+
path: path.slice(0, 512),
|
|
230
|
+
change,
|
|
231
|
+
bytes: content === null ? null : Buffer.byteLength(content),
|
|
232
|
+
preview: content === null ? null : content.slice(0, 1200),
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
};
|
|
236
|
+
}
|