@superjackfruitlabs/salaam 0.0.1 → 0.0.2
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 +15 -0
- package/README.md +25 -17
- package/dist/src/a2a-backend.js +225 -0
- package/dist/src/a2a-inbound.js +487 -0
- package/dist/src/a2a-peer.js +370 -0
- package/dist/src/a2a-protocol.js +250 -0
- package/dist/src/agent-profiles.js +289 -0
- package/dist/src/children.js +132 -26
- package/dist/src/claude-provider.js +2 -179
- package/dist/src/cli-help.js +107 -6
- package/dist/src/cli.js +477 -13
- package/dist/src/config.js +26 -3
- package/dist/src/credentials.js +7 -2
- package/dist/src/external-runtime.js +1 -0
- package/dist/src/external-work.js +583 -0
- package/dist/src/harness-backend.js +466 -0
- package/dist/src/harness-container.js +450 -0
- package/dist/src/harness-model-gateway.js +104 -0
- package/dist/src/harness-prepare.js +88 -0
- package/dist/src/harness-probe.js +111 -0
- package/dist/src/harness-protocol.js +325 -0
- package/dist/src/harness-readiness.js +50 -0
- package/dist/src/harness-rpc.js +206 -0
- package/dist/src/harness-tool-identity.js +87 -0
- package/dist/src/harness-wire-bridge.js +387 -0
- package/dist/src/host.js +6 -1
- package/dist/src/house-extension.js +147 -64
- package/dist/src/live-provider.js +301 -0
- package/dist/src/managed.js +33 -3
- package/dist/src/protocol.js +9 -5
- package/dist/src/provider-auth.js +300 -0
- package/dist/src/provider-config.js +65 -17
- package/dist/src/provider-model.js +78 -0
- package/dist/src/provider-payload.js +264 -0
- package/dist/src/provider-transport.js +36 -0
- package/dist/src/provider-usage.js +59 -0
- package/dist/src/repository-snapshots.js +219 -0
- package/dist/src/service.js +274 -9
- package/dist/src/store.js +44 -2
- package/dist/src/supervisor.js +246 -35
- package/dist/src/task-budget.js +17 -8
- package/dist/src/task-context.js +15 -0
- package/dist/src/task-driver.js +38 -14
- package/dist/src/task-policy.js +20 -1
- package/dist/src/task-verification.js +3 -0
- package/dist/src/task-work.js +31 -0
- package/dist/src/team-work.js +378 -0
- package/dist/src/tui-view.js +65 -10
- package/dist/src/tui.js +4 -2
- package/dist/src/usage.js +18 -0
- package/dist/src/worker-boundary.js +298 -0
- package/dist/src/worker-workspaces.js +274 -0
- package/dist/src/workspaces.js +6 -2
- package/harness-runtime/codex-pins.json +10 -0
- package/harness-runtime/package-lock.json +560 -0
- package/harness-runtime/package.json +9 -0
- package/npm-shrinkwrap.json +57 -2
- package/package.json +2 -1
- package/src/a2a-backend.ts +254 -0
- package/src/a2a-inbound.ts +585 -0
- package/src/a2a-peer.ts +441 -0
- package/src/a2a-protocol.ts +286 -0
- package/src/agent-profiles.ts +379 -0
- package/src/children.ts +168 -27
- package/src/claude-provider.ts +2 -189
- package/src/cli-help.ts +107 -6
- package/src/cli.ts +602 -12
- package/src/config.ts +34 -3
- package/src/credentials.ts +9 -2
- package/src/external-runtime.ts +54 -0
- package/src/external-work.ts +663 -0
- package/src/harness-backend.ts +557 -0
- package/src/harness-container.ts +543 -0
- package/src/harness-model-gateway.ts +138 -0
- package/src/harness-prepare.ts +113 -0
- package/src/harness-probe.ts +150 -0
- package/src/harness-protocol.ts +388 -0
- package/src/harness-readiness.ts +71 -0
- package/src/harness-rpc.ts +244 -0
- package/src/harness-tool-identity.ts +91 -0
- package/src/harness-wire-bridge.ts +418 -0
- package/src/host.ts +11 -6
- package/src/house-extension.ts +165 -66
- package/src/live-provider.ts +347 -0
- package/src/managed.ts +43 -4
- package/src/protocol.ts +36 -8
- package/src/provider-auth.ts +336 -0
- package/src/provider-config.ts +89 -18
- package/src/provider-model.ts +86 -0
- package/src/provider-payload.ts +263 -0
- package/src/provider-transport.ts +51 -0
- package/src/provider-usage.ts +64 -0
- package/src/repository-snapshots.ts +259 -0
- package/src/service.ts +446 -11
- package/src/store.ts +60 -4
- package/src/supervisor.ts +294 -41
- package/src/task-budget.ts +24 -11
- package/src/task-context.ts +17 -0
- package/src/task-driver.ts +51 -15
- package/src/task-policy.ts +23 -3
- package/src/task-verification.ts +2 -0
- package/src/task-work.ts +46 -1
- package/src/team-work.ts +554 -0
- package/src/tui-client.ts +5 -0
- package/src/tui-view.ts +164 -12
- package/src/tui.ts +5 -2
- package/src/usage.ts +17 -0
- package/src/worker-boundary.ts +395 -0
- package/src/worker-workspaces.ts +390 -0
- package/src/workspaces.ts +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,21 @@ are forward-only: keep a stopped-daemon backup before upgrading.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.0.2] - 2026-10-19
|
|
10
|
+
|
|
11
|
+
- Add the complete pinned Pi provider catalog, model listing, API-key and OAuth setup, and cloud credentials.
|
|
12
|
+
- Validate all ten chat APIs at dispatch, preserve provider-bound recovery and usage, and allow uncapped subscription routes for durable tasks with an explicit token-budget opt-out, while preserving request/runtime limits and rejecting unsupported token caps.
|
|
13
|
+
- Add admitted native specialist profiles, immutable repository evidence, reviewer
|
|
14
|
+
findings, durable task-step assignments and passive team messages.
|
|
15
|
+
- Give coding specialists private drafts with explicit hash-bound integration,
|
|
16
|
+
protected human edits and fresh parent acceptance checks.
|
|
17
|
+
- Add pinned Gemini ACP, Claude ACP and Codex app-server workers with isolated
|
|
18
|
+
execution, concrete shared approvals and durable invocation/recovery records.
|
|
19
|
+
- Add pinned A2A analysis peers, private one-use inbound grants, owned task
|
|
20
|
+
reconciliation and named Hermes/OpenClaw transport interoperability.
|
|
21
|
+
- Show native, harness and peer workers together in the Salaam terminal, with
|
|
22
|
+
saved identities across disconnects and explicit recovery of uncertain work.
|
|
23
|
+
|
|
9
24
|
## [0.0.1] - 2026-10-18
|
|
10
25
|
|
|
11
26
|
Initial release preparation. A version entry is not a published release; see GitHub
|
package/README.md
CHANGED
|
@@ -38,27 +38,30 @@ Use **Node 26.7.0** and npm **11.19.0**. macOS and Linux are supported; Windows
|
|
|
38
38
|
not currently tested. Packages include compiled CLI code and pinned production
|
|
39
39
|
dependencies. No compiler, checkout or database server is needed.
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
inspect it, then run:
|
|
41
|
+
Install the latest stable release:
|
|
43
42
|
|
|
44
43
|
```sh
|
|
45
|
-
sh
|
|
44
|
+
curl -fsSL https://salaam.sh/install.sh | sh
|
|
46
45
|
salaam --version
|
|
47
46
|
sal --help
|
|
48
47
|
```
|
|
49
48
|
|
|
50
49
|
The installer verifies the release tarball's SHA-256 checksum and installs both
|
|
51
|
-
executable names.
|
|
52
|
-
|
|
50
|
+
executable names. You can [read the installer](https://salaam.sh/install.sh) before
|
|
51
|
+
running it. To choose a version or private prefix, pass arguments to `sh`:
|
|
53
52
|
|
|
54
|
-
|
|
53
|
+
```sh
|
|
54
|
+
curl -fsSL https://salaam.sh/install.sh | sh -s -- --version 0.0.2 --prefix "$HOME/.local"
|
|
55
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Or install from npm:
|
|
55
59
|
|
|
56
60
|
```sh
|
|
57
|
-
npm install --global @superjackfruitlabs/salaam@0.0.
|
|
61
|
+
npm install --global @superjackfruitlabs/salaam@0.0.2 --ignore-scripts
|
|
58
62
|
```
|
|
59
63
|
|
|
60
|
-
|
|
61
|
-
npm for availability. See [installation and upgrades](docs/install.md) for direct
|
|
64
|
+
See [installation and upgrades](docs/install.md) for direct
|
|
62
65
|
tarball installation, prerequisites and safe state migration.
|
|
63
66
|
|
|
64
67
|
## Your first session
|
|
@@ -75,6 +78,8 @@ sal attach SESSION_ID --state /tmp/salaam-try
|
|
|
75
78
|
Replace `SESSION_ID` with the returned ID. Enter an instruction, detach with
|
|
76
79
|
Ctrl+D, then attach again. Ctrl+K opens actions; Ctrl+O opens approvals.
|
|
77
80
|
|
|
81
|
+
Choose a provider with `sal providers list` and a model with `sal models list PROVIDER`. Salaam exposes all 41 chat providers in pinned Pi, including API-key, OAuth and cloud authentication. ChatGPT subscription routes support interactive sessions and durable tasks that explicitly disable token budgeting; these routes cannot enforce an output-token ceiling.
|
|
82
|
+
|
|
78
83
|
For Claude, use the hidden terminal prompt and a separate provider-bound service:
|
|
79
84
|
|
|
80
85
|
```sh
|
|
@@ -89,14 +94,17 @@ by OS permissions. See [providers and credentials](docs/providers.md).
|
|
|
89
94
|
|
|
90
95
|
## What stays with the work
|
|
91
96
|
|
|
92
|
-
| Capability
|
|
93
|
-
|
|
|
94
|
-
| [Sessions](docs/service-reference.md)
|
|
95
|
-
| [Coding](docs/coding.md)
|
|
96
|
-
| [Tasks](docs/durable-tasks.md)
|
|
97
|
-
| [
|
|
98
|
-
| [
|
|
99
|
-
| [
|
|
97
|
+
| Capability | What it means |
|
|
98
|
+
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
99
|
+
| [Sessions](docs/service-reference.md) | Durable dispatch, client outboxes, controller leases and committed event replay. |
|
|
100
|
+
| [Coding](docs/coding.md) | Owned workspace copies, hash-bound patches and isolated commands through a proven Docker backend. |
|
|
101
|
+
| [Tasks](docs/durable-tasks.md) | Stable steps, checked plans, budgets, checkpoints and opt-in bounded progression. Model text cannot complete a checked task. |
|
|
102
|
+
| [Specialists and teams](docs/agents.md) | Admitted profiles, isolated coding proposals, explicit integration, durable task-step claims and passive messages. |
|
|
103
|
+
| [External harnesses](docs/external-harnesses.md) | Pinned Gemini, Claude ACP and Codex workers with explicit invocation limits and shared approvals. |
|
|
104
|
+
| [A2A peers](docs/a2a.md) | Authenticated, bounded peer analysis, explicit inbound grants and durable task ownership. |
|
|
105
|
+
| [Jobs and schedules](docs/jobs.md) | Occurrence deduplication, immutable recurring templates and explicit missed-run and timezone/DST policies. |
|
|
106
|
+
| [Host operation](docs/host-operation.md) | Reviewed runtime/backend manifests, bounded supervisor restart and a shared CLI/TUI/ACP inbox. |
|
|
107
|
+
| [Clients](docs/clients.md) | Terminal attachment, SSH and ACP v1 against the same service. |
|
|
100
108
|
|
|
101
109
|
## Read this before trusting it
|
|
102
110
|
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { canonical, digest, ServiceError } from './protocol.js';
|
|
2
|
+
import { hostDigests } from './host.js';
|
|
3
|
+
import { A2AHttp, A2A_PIN, a2aObject, privateJSON, validatePeer, } from './a2a-peer.js';
|
|
4
|
+
import { A2AInvocation } from './a2a-protocol.js';
|
|
5
|
+
/** Bounded outbound analysis. A peer has no local file/command/controller authority. */
|
|
6
|
+
export class A2ABackend {
|
|
7
|
+
config;
|
|
8
|
+
peers;
|
|
9
|
+
ready = false;
|
|
10
|
+
active = new Map();
|
|
11
|
+
trees = new Map();
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
this.peers = this.read();
|
|
15
|
+
}
|
|
16
|
+
read() {
|
|
17
|
+
const config = a2aObject(privateJSON(this.config));
|
|
18
|
+
if (Object.keys(config).sort().join(',') !== 'peers,schema' ||
|
|
19
|
+
config.schema !== 1 ||
|
|
20
|
+
!Array.isArray(config.peers) ||
|
|
21
|
+
!config.peers.length ||
|
|
22
|
+
config.peers.length > 16)
|
|
23
|
+
throw Error('Expected bounded A2A peer registry schema 1');
|
|
24
|
+
const peers = config.peers.map(validatePeer);
|
|
25
|
+
if (new Set(peers.map((p) => p.id)).size !== peers.length)
|
|
26
|
+
throw Error('Duplicate peer identity');
|
|
27
|
+
return peers;
|
|
28
|
+
}
|
|
29
|
+
descriptor(p) {
|
|
30
|
+
return {
|
|
31
|
+
id: p.id,
|
|
32
|
+
kind: 'a2a',
|
|
33
|
+
target: 'a2a',
|
|
34
|
+
version: A2A_PIN.wire,
|
|
35
|
+
config_digest: digest(p),
|
|
36
|
+
adapter_digest: hostDigests().build_digest,
|
|
37
|
+
execution_identity: {
|
|
38
|
+
version: A2A_PIN.wire,
|
|
39
|
+
kernel: 'remote-unobserved',
|
|
40
|
+
arch: 'remote-unobserved',
|
|
41
|
+
},
|
|
42
|
+
boundary_digest: digest({ protocol: A2A_PIN, peer: p, authority: 'remote-analysis-only' }),
|
|
43
|
+
engine_id: p.id,
|
|
44
|
+
evidence: 'proven',
|
|
45
|
+
model_usage: 'unknown',
|
|
46
|
+
output_cap: null,
|
|
47
|
+
max_model_requests: null,
|
|
48
|
+
files: false,
|
|
49
|
+
commands: false,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
async prepare() {
|
|
53
|
+
this.peers = this.read();
|
|
54
|
+
this.ready = true;
|
|
55
|
+
}
|
|
56
|
+
async close() {
|
|
57
|
+
this.active.clear();
|
|
58
|
+
this.trees.clear();
|
|
59
|
+
this.ready = false;
|
|
60
|
+
}
|
|
61
|
+
runtimes() {
|
|
62
|
+
return this.ready ? this.peers.map((p) => this.descriptor(p)) : [];
|
|
63
|
+
}
|
|
64
|
+
validate(runtime) {
|
|
65
|
+
const p = this.read().find((p) => p.id === runtime.id);
|
|
66
|
+
if (!this.ready || !p || canonical(this.descriptor(p)) !== canonical(runtime))
|
|
67
|
+
throw Error('A2A peer/config/credential/build admission changed');
|
|
68
|
+
}
|
|
69
|
+
assertAuthority(_runtime, tools) {
|
|
70
|
+
if (tools.some((t) => [
|
|
71
|
+
'salaam_files',
|
|
72
|
+
'salaam_command',
|
|
73
|
+
'salaam_team',
|
|
74
|
+
'salaam_managed',
|
|
75
|
+
'salaam_children',
|
|
76
|
+
].includes(t)))
|
|
77
|
+
throw Error('A2A peers are analysis specialists without local tools');
|
|
78
|
+
}
|
|
79
|
+
async execute(binding, run, prompt, tree, hooks) {
|
|
80
|
+
this.validate(binding.external);
|
|
81
|
+
this.assertAuthority(binding.external, binding.tools);
|
|
82
|
+
hooks.assertActive();
|
|
83
|
+
if (this.active.has(run) || this.trees.has(run))
|
|
84
|
+
throw Error('A2A invocation cannot be replayed');
|
|
85
|
+
const p = this.peers.find((p) => p.id === binding.external.id), http = new A2AHttp(p);
|
|
86
|
+
const files = Object.create(null), disclosure = [];
|
|
87
|
+
for (const path of p.files) {
|
|
88
|
+
const f = tree[path];
|
|
89
|
+
if (!f)
|
|
90
|
+
throw Error('An explicitly disclosed repository file is absent');
|
|
91
|
+
const bytes = Buffer.from(f.content, 'base64'), text = bytes.toString('utf8');
|
|
92
|
+
if (!Buffer.from(text).equals(bytes) || bytes.length > 65536)
|
|
93
|
+
throw Error('Only bounded UTF-8 peer disclosure is supported');
|
|
94
|
+
files[path] = text;
|
|
95
|
+
disclosure.push({ path, digest: digest(f), bytes: bytes.length });
|
|
96
|
+
}
|
|
97
|
+
const data = { skill: p.skill, files }, peer = { id: p.id, tenant: p.tenant, digest: digest(p), cancel: p.cancel, skill: p.skill };
|
|
98
|
+
if (canonical({ prompt, data }).length > 196608)
|
|
99
|
+
throw Error('A2A disclosure exceeds bound');
|
|
100
|
+
const initial = A2AInvocation.prepare(binding.worker_id, binding.attempt_id, peer, prompt, data, disclosure);
|
|
101
|
+
initial.run_id = run;
|
|
102
|
+
// Both identity and disclosure are durable before any SendMessage bytes leave.
|
|
103
|
+
hooks.checkpoint(initial);
|
|
104
|
+
hooks.receipt(initial);
|
|
105
|
+
const invocation = new A2AInvocation(peer, initial, http, (cp) => {
|
|
106
|
+
hooks.checkpoint(cp);
|
|
107
|
+
hooks.receipt(cp);
|
|
108
|
+
return undefined;
|
|
109
|
+
});
|
|
110
|
+
this.active.set(run, invocation);
|
|
111
|
+
this.trees.set(run, structuredClone(tree));
|
|
112
|
+
await http.card();
|
|
113
|
+
hooks.assertActive();
|
|
114
|
+
let task = await invocation.dispatch(prompt, data);
|
|
115
|
+
const end = Date.now() + 180000;
|
|
116
|
+
while (invocation.view().state !== 'terminal') {
|
|
117
|
+
hooks.assertActive();
|
|
118
|
+
this.validate(binding.external);
|
|
119
|
+
if (['TASK_STATE_INPUT_REQUIRED', 'TASK_STATE_AUTH_REQUIRED'].includes(task.status.state))
|
|
120
|
+
throw Error('Peer needs input/authentication; explicit operator resolution required');
|
|
121
|
+
if (Date.now() >= end)
|
|
122
|
+
throw Error('Peer deadline reached; remote work may still run');
|
|
123
|
+
if (p.streaming) {
|
|
124
|
+
try {
|
|
125
|
+
await http.subscribe(invocation.view().task_id, (event) => {
|
|
126
|
+
hooks.assertActive();
|
|
127
|
+
invocation.observe(event);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
if (error instanceof ServiceError)
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else
|
|
136
|
+
await new Promise((r) => setTimeout(r, 2500));
|
|
137
|
+
hooks.assertActive();
|
|
138
|
+
task = await invocation.inspect();
|
|
139
|
+
}
|
|
140
|
+
if (task.status.state !== 'TASK_STATE_COMPLETED')
|
|
141
|
+
throw Error('Remote task ended without a completed result');
|
|
142
|
+
return {
|
|
143
|
+
result: {
|
|
144
|
+
peer: p.id,
|
|
145
|
+
tenant: p.tenant,
|
|
146
|
+
task,
|
|
147
|
+
disclosure,
|
|
148
|
+
prompt_digest: digest(prompt),
|
|
149
|
+
verified_success: false,
|
|
150
|
+
remote_execution: 'unobserved',
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
collect(run) {
|
|
155
|
+
const tree = this.trees.get(run);
|
|
156
|
+
if (!tree)
|
|
157
|
+
throw Error('No owned local snapshot for remote analysis');
|
|
158
|
+
this.trees.delete(run);
|
|
159
|
+
return tree;
|
|
160
|
+
}
|
|
161
|
+
async stop(run, runtime, receipt) {
|
|
162
|
+
this.validate(runtime);
|
|
163
|
+
let invocation = this.active.get(run);
|
|
164
|
+
if (!invocation) {
|
|
165
|
+
const record = a2aObject(receipt);
|
|
166
|
+
const cp = a2aObject(record.checkpoint ?? record), p = this.peers.find((p) => p.id === runtime.id);
|
|
167
|
+
if (cp.run_id !== run)
|
|
168
|
+
throw Error('Remote cleanup receipt belongs to another local run');
|
|
169
|
+
invocation = new A2AInvocation({ id: p.id, tenant: p.tenant, digest: digest(p), cancel: p.cancel, skill: p.skill }, cp, new A2AHttp(p), () => undefined);
|
|
170
|
+
}
|
|
171
|
+
if (invocation.view().state === 'prepared') {
|
|
172
|
+
this.active.delete(run);
|
|
173
|
+
this.trees.delete(run);
|
|
174
|
+
return { scope: 'never-dispatched', checkpoint: invocation.view() };
|
|
175
|
+
}
|
|
176
|
+
await invocation.cancel();
|
|
177
|
+
if (invocation.view().state !== 'terminal')
|
|
178
|
+
throw Error('Remote terminal receipt is unconfirmed');
|
|
179
|
+
this.active.delete(run);
|
|
180
|
+
if (invocation.view().result?.status.state !== 'TASK_STATE_COMPLETED')
|
|
181
|
+
this.trees.delete(run);
|
|
182
|
+
return { scope: 'remote-terminal-receipt', checkpoint: invocation.view() };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
export class ExternalBackends {
|
|
186
|
+
backends;
|
|
187
|
+
constructor(backends) {
|
|
188
|
+
this.backends = backends;
|
|
189
|
+
}
|
|
190
|
+
async prepare() {
|
|
191
|
+
for (const b of this.backends)
|
|
192
|
+
await b.prepare?.();
|
|
193
|
+
const ids = this.runtimes().map((r) => r.id);
|
|
194
|
+
if (new Set(ids).size !== ids.length)
|
|
195
|
+
throw Error('Runtime IDs must be unique across local and remote backends');
|
|
196
|
+
}
|
|
197
|
+
async close() {
|
|
198
|
+
for (const b of this.backends)
|
|
199
|
+
await b.close?.();
|
|
200
|
+
}
|
|
201
|
+
runtimes() {
|
|
202
|
+
return this.backends.flatMap((b) => b.runtimes());
|
|
203
|
+
}
|
|
204
|
+
owner(runtime) {
|
|
205
|
+
const b = this.backends.find((b) => b.runtimes().some((r) => r.id === runtime.id));
|
|
206
|
+
if (!b)
|
|
207
|
+
throw Error('Runtime is unavailable');
|
|
208
|
+
return b;
|
|
209
|
+
}
|
|
210
|
+
validate(runtime) {
|
|
211
|
+
this.owner(runtime).validate(runtime);
|
|
212
|
+
}
|
|
213
|
+
assertAuthority(runtime, tools) {
|
|
214
|
+
this.owner(runtime).assertAuthority?.(runtime, tools);
|
|
215
|
+
}
|
|
216
|
+
execute(binding, run, prompt, tree, hooks) {
|
|
217
|
+
return this.owner(binding.external).execute(binding, run, prompt, tree, hooks);
|
|
218
|
+
}
|
|
219
|
+
collect(run, runtime) {
|
|
220
|
+
return this.owner(runtime).collect(run, runtime);
|
|
221
|
+
}
|
|
222
|
+
stop(run, runtime, receipt) {
|
|
223
|
+
return this.owner(runtime).stop(run, runtime, receipt);
|
|
224
|
+
}
|
|
225
|
+
}
|