@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/src/tui-view.ts
CHANGED
|
@@ -29,7 +29,11 @@ export function modelLabel(client: OperatorClient): string {
|
|
|
29
29
|
const route = client.health?.provider_route;
|
|
30
30
|
if (!route) return 'Connect your workspace';
|
|
31
31
|
if (['offline', 'salaam-proof'].includes(route.provider)) return 'Offline demo';
|
|
32
|
-
|
|
32
|
+
const warning =
|
|
33
|
+
client.health?.provider_capabilities?.output_token_cap === false ? ' · no output cap' : '';
|
|
34
|
+
return (
|
|
35
|
+
route.model.replace('claude-sonnet-', 'Claude Sonnet ').replace(/(\d)-(\d)$/, '$1.$2') + warning
|
|
36
|
+
);
|
|
33
37
|
}
|
|
34
38
|
export class Dynamic implements Component {
|
|
35
39
|
constructor(private readonly content: (width: number) => string[]) {}
|
|
@@ -507,6 +511,107 @@ export class DocumentView implements Component {
|
|
|
507
511
|
...this.text(job.reason, width),
|
|
508
512
|
);
|
|
509
513
|
}
|
|
514
|
+
const team = c.snapshot.team as
|
|
515
|
+
| {
|
|
516
|
+
assignments: {
|
|
517
|
+
id: string;
|
|
518
|
+
profile_id: string;
|
|
519
|
+
generation: number;
|
|
520
|
+
step_state: string;
|
|
521
|
+
stale: boolean;
|
|
522
|
+
runtime?: { id: string; provider_route?: { provider: string; model: string } } | null;
|
|
523
|
+
execution?: {
|
|
524
|
+
state: string;
|
|
525
|
+
stopped: boolean;
|
|
526
|
+
stop_scope?: string;
|
|
527
|
+
peer?: {
|
|
528
|
+
peer_id: string;
|
|
529
|
+
task_id: string | null;
|
|
530
|
+
cancel_state: string | null;
|
|
531
|
+
remote_state: string | null;
|
|
532
|
+
} | null;
|
|
533
|
+
} | null;
|
|
534
|
+
}[];
|
|
535
|
+
messages: {
|
|
536
|
+
id: string;
|
|
537
|
+
sender: string;
|
|
538
|
+
recipient: string;
|
|
539
|
+
delivered_at: number | null;
|
|
540
|
+
acknowledged_at: number | null;
|
|
541
|
+
}[];
|
|
542
|
+
}
|
|
543
|
+
| undefined;
|
|
544
|
+
if (team?.assignments.length) {
|
|
545
|
+
lines.push(...this.title('TEAM BOARD', width));
|
|
546
|
+
for (const a of team.assignments) {
|
|
547
|
+
lines.push(
|
|
548
|
+
...this.text(
|
|
549
|
+
`${shortId(a.id)} · ${a.profile_id} · claim ${a.generation} · ${a.step_state}${a.stale ? ' · stale plan' : ''}`,
|
|
550
|
+
width,
|
|
551
|
+
),
|
|
552
|
+
);
|
|
553
|
+
if (a.runtime)
|
|
554
|
+
lines.push(
|
|
555
|
+
...this.text(a.runtime.id + ' · invocation budget · model usage unknown', width),
|
|
556
|
+
);
|
|
557
|
+
if (a.execution?.peer)
|
|
558
|
+
lines.push(
|
|
559
|
+
...this.text(
|
|
560
|
+
'Peer ' +
|
|
561
|
+
a.execution.peer.peer_id +
|
|
562
|
+
' · task ' +
|
|
563
|
+
(a.execution.peer.task_id ?? 'acceptance unknown'),
|
|
564
|
+
width,
|
|
565
|
+
),
|
|
566
|
+
...this.text(
|
|
567
|
+
(a.execution.peer.remote_state ?? 'remote state unknown') +
|
|
568
|
+
' · ' +
|
|
569
|
+
(a.execution.peer.cancel_state ?? 'no cancel requested'),
|
|
570
|
+
width,
|
|
571
|
+
),
|
|
572
|
+
);
|
|
573
|
+
if (a.runtime?.provider_route)
|
|
574
|
+
lines.push(
|
|
575
|
+
...this.text(
|
|
576
|
+
'Backend ' +
|
|
577
|
+
a.runtime.provider_route.provider +
|
|
578
|
+
'/' +
|
|
579
|
+
a.runtime.provider_route.model,
|
|
580
|
+
width,
|
|
581
|
+
),
|
|
582
|
+
);
|
|
583
|
+
if (a.execution)
|
|
584
|
+
lines.push(
|
|
585
|
+
...this.text(
|
|
586
|
+
a.execution.state +
|
|
587
|
+
' · ' +
|
|
588
|
+
(a.execution.stop_scope === 'never-dispatched'
|
|
589
|
+
? 'not dispatched'
|
|
590
|
+
: a.execution.stop_scope === 'remote-terminal-receipt'
|
|
591
|
+
? a.execution.stopped
|
|
592
|
+
? 'remote terminal receipt'
|
|
593
|
+
: 'remote stop unconfirmed'
|
|
594
|
+
: a.execution.stopped
|
|
595
|
+
? 'stop confirmed'
|
|
596
|
+
: 'stop unconfirmed'),
|
|
597
|
+
width,
|
|
598
|
+
),
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
lines.push(
|
|
602
|
+
...this.text(
|
|
603
|
+
'Messages are passive. Starting or continuing work requires a separate grant.',
|
|
604
|
+
width,
|
|
605
|
+
),
|
|
606
|
+
);
|
|
607
|
+
for (const m of team.messages)
|
|
608
|
+
lines.push(
|
|
609
|
+
...this.text(
|
|
610
|
+
`${shortId(m.id)} · ${m.sender === 'operator' ? 'operator' : shortId(m.sender)} → ${shortId(m.recipient)} · ${m.acknowledged_at ? 'acknowledged' : m.delivered_at ? 'delivered' : 'stored'}`,
|
|
611
|
+
width,
|
|
612
|
+
),
|
|
613
|
+
);
|
|
614
|
+
}
|
|
510
615
|
const task = c.snapshot.work_task;
|
|
511
616
|
if (task) {
|
|
512
617
|
lines.push(
|
|
@@ -530,12 +635,17 @@ export class DocumentView implements Component {
|
|
|
530
635
|
width,
|
|
531
636
|
),
|
|
532
637
|
...this.text(
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
638
|
+
task.limits.token_units === null
|
|
639
|
+
? 'Token budget disabled · ' +
|
|
640
|
+
(task.limits.output_token_bound === null
|
|
641
|
+
? 'no task output-token bound'
|
|
642
|
+
: 'output bound ' + task.limits.output_token_bound)
|
|
643
|
+
: 'Declared token units ' +
|
|
644
|
+
task.runtime.token_units +
|
|
645
|
+
' + ' +
|
|
646
|
+
task.runtime.reserved_tokens +
|
|
647
|
+
' reserved / ' +
|
|
648
|
+
task.limits.token_units,
|
|
539
649
|
width,
|
|
540
650
|
),
|
|
541
651
|
...this.text(
|
|
@@ -547,11 +657,34 @@ export class DocumentView implements Component {
|
|
|
547
657
|
width,
|
|
548
658
|
),
|
|
549
659
|
);
|
|
550
|
-
if (task.verification)
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
660
|
+
if (task.verification) {
|
|
661
|
+
const verification = task.verification as {
|
|
662
|
+
checks?: {
|
|
663
|
+
id: string;
|
|
664
|
+
check_key: string;
|
|
665
|
+
state: string;
|
|
666
|
+
fresh: boolean;
|
|
667
|
+
workspace_revision: number;
|
|
668
|
+
}[];
|
|
669
|
+
completion?: { state: string; fresh: boolean } | null;
|
|
670
|
+
};
|
|
671
|
+
lines.push(...this.title('VERIFICATION', width));
|
|
672
|
+
for (const check of verification.checks ?? [])
|
|
673
|
+
lines.push(
|
|
674
|
+
...this.text(
|
|
675
|
+
`${check.check_key} · ${check.state} · ${check.fresh ? 'current' : 'historical'} · r${check.workspace_revision} · ${shortId(check.id)}`,
|
|
676
|
+
width,
|
|
677
|
+
),
|
|
678
|
+
);
|
|
679
|
+
if (verification.completion)
|
|
680
|
+
lines.push(
|
|
681
|
+
...this.text(
|
|
682
|
+
`Task ${verification.completion.state} · ${verification.completion.fresh ? 'current verification' : 'verification needs refresh'}`,
|
|
683
|
+
width,
|
|
684
|
+
),
|
|
685
|
+
);
|
|
686
|
+
lines.push(...this.text('Ctrl+K → Inspect task for complete check receipts.', width));
|
|
687
|
+
}
|
|
555
688
|
for (const constraint of task.constraints)
|
|
556
689
|
lines.push(...this.text('Constraint: ' + constraint, width));
|
|
557
690
|
for (const step of task.steps)
|
|
@@ -585,6 +718,13 @@ export class DocumentView implements Component {
|
|
|
585
718
|
t.fill('', width),
|
|
586
719
|
);
|
|
587
720
|
lines.push(...this.title('CHILD WORK', width));
|
|
721
|
+
for (const agent of c.snapshot.agents ?? [])
|
|
722
|
+
lines.push(
|
|
723
|
+
...this.text(
|
|
724
|
+
`${agent.profile_id} · revision ${agent.revision} · ${agent.state} · ${agent.route.provider}/${agent.route.model} · ${agent.tools.join(', ')}`,
|
|
725
|
+
width,
|
|
726
|
+
),
|
|
727
|
+
);
|
|
588
728
|
if (!c.snapshot.children.length)
|
|
589
729
|
lines.push(...this.text('No child work in this session.', width));
|
|
590
730
|
for (const child of c.snapshot.children)
|
|
@@ -598,6 +738,18 @@ export class DocumentView implements Component {
|
|
|
598
738
|
(child.acknowledged ? ' · acknowledged' : ''),
|
|
599
739
|
width,
|
|
600
740
|
),
|
|
741
|
+
...(child.worker
|
|
742
|
+
? this.text(
|
|
743
|
+
`${child.worker.binding.profile.id} · ${child.worker.binding.runtime} · generation ${child.worker.generation} · ${child.worker.dispatch_state} · ${child.worker.binding.bounds.scope === 'model-requests' ? child.worker.binding.bounds.requests + ' model requests' : child.worker.binding.bounds.invocations + ' invocation · internal model usage unknown'}`,
|
|
744
|
+
width,
|
|
745
|
+
)
|
|
746
|
+
: []),
|
|
747
|
+
...(child.worker?.binding.isolation
|
|
748
|
+
? this.text(
|
|
749
|
+
`Private draft ${shortId(child.worker.binding.isolation.workspace_id)} · execution ${child.worker.binding.isolation.execution.evidence} · root integration requires a reviewed proposal digest`,
|
|
750
|
+
width,
|
|
751
|
+
)
|
|
752
|
+
: []),
|
|
601
753
|
);
|
|
602
754
|
lines.push(t.fill('', width), ...this.title('USAGE', width));
|
|
603
755
|
if (c.usage)
|
package/src/tui.ts
CHANGED
|
@@ -410,7 +410,10 @@ export class SalaamApp {
|
|
|
410
410
|
private async startService(): Promise<void> {
|
|
411
411
|
await this.client.list();
|
|
412
412
|
if (this.client.connection === 'connected') return;
|
|
413
|
-
const provider =
|
|
413
|
+
const provider =
|
|
414
|
+
existsSync(credentialPath()) || existsSync(credentialPath() + '.json')
|
|
415
|
+
? 'anthropic'
|
|
416
|
+
: 'offline';
|
|
414
417
|
await promisify(execFile)(
|
|
415
418
|
process.execPath,
|
|
416
419
|
[
|
|
@@ -826,7 +829,7 @@ export class SalaamApp {
|
|
|
826
829
|
const max_steps = Number(value);
|
|
827
830
|
this.confirm(
|
|
828
831
|
'Run this bounded plan',
|
|
829
|
-
`Task ${shortId(task.id)} · plan revision ${task.plan_revision}\nAt most ${max_steps} new steps; each step needs fresh passing checks.\nLimits: ${task.limits.requests} requests · ${task.limits.runtime_ms} ms · ${task.limits.concurrency} concurrent executions.\n${task.limits.token_units
|
|
832
|
+
`Task ${shortId(task.id)} · plan revision ${task.plan_revision}\nAt most ${max_steps} new steps; each step needs fresh passing checks.\nLimits: ${task.limits.requests} requests · ${task.limits.runtime_ms} ms · ${task.limits.concurrency} concurrent executions.\n${task.limits.token_units === null ? 'Token budget disabled' : task.limits.token_units + ' declared token units'} · ${task.limits.verification_runs} verification runs.\nChecks: ${task.acceptance.map((c) => c.key).join(', ') || 'none'} · Deliverables: ${task.deliverables.length}.\nDeterministic compaction at idle boundaries. Human approvals remain separate.`,
|
|
830
833
|
'Run bounded task',
|
|
831
834
|
() => this.client.taskAction('auto', { max_steps, compact: true }, reviewed),
|
|
832
835
|
);
|
package/src/usage.ts
CHANGED
|
@@ -115,6 +115,23 @@ export class UsageLedger {
|
|
|
115
115
|
typeof m?.responseId === 'string' && m.responseId.startsWith('salaam-request:')
|
|
116
116
|
? m.responseId
|
|
117
117
|
: `pi:${session}:${entry.id}`;
|
|
118
|
+
// Managed live streams report receipts through the authenticated bridge.
|
|
119
|
+
// Pi normalizes a missing receipt to zeros; importing that transcript must
|
|
120
|
+
// never upgrade a conservatively unknown observation to a zero charge.
|
|
121
|
+
if (key.startsWith('salaam-request:')) {
|
|
122
|
+
const managed = this.store.db
|
|
123
|
+
.prepare('SELECT session_id,provider FROM usage_observations WHERE source_key=?')
|
|
124
|
+
.get(key);
|
|
125
|
+
if (managed && managed.provider !== 'salaam-proof') {
|
|
126
|
+
if (managed.session_id !== session)
|
|
127
|
+
throw new ServiceError('invalid_usage', 'Usage does not belong to this session');
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
// A rejected live request can still produce a Pi error transcript. Its
|
|
131
|
+
// absence from admission is definitive: no request was dispatched.
|
|
132
|
+
const route = this.store.providerRoute(session);
|
|
133
|
+
if (!managed && route && route.provider !== 'salaam-proof') continue;
|
|
134
|
+
}
|
|
118
135
|
// An aborted provider message may carry placeholder zeros. They are not evidence of zero spend.
|
|
119
136
|
const complete = m && !['error', 'aborted'].includes(String(m.stopReason));
|
|
120
137
|
if ((complete && m.usage) || entry.usage)
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import type { Store } from './store.js';
|
|
3
|
+
import { canonical, digest, textDigest, ServiceError, type ChildTask } from './protocol.js';
|
|
4
|
+
import { POLICY_DIGEST } from './config.js';
|
|
5
|
+
import type { ProviderRoute } from './provider-config.js';
|
|
6
|
+
import type { AgentProfile, AgentGrant, AgentProfiles } from './agent-profiles.js';
|
|
7
|
+
import type { ExternalRuntime } from './external-runtime.js';
|
|
8
|
+
|
|
9
|
+
/** Local contract v1. These are enforced capabilities, not upstream feature claims. */
|
|
10
|
+
export const NATIVE_CAPABILITIES = {
|
|
11
|
+
schema_version: 1,
|
|
12
|
+
runtime: 'native-pi',
|
|
13
|
+
model_requests: true,
|
|
14
|
+
output_cap: 'authentication-dependent',
|
|
15
|
+
usage_receipts: true,
|
|
16
|
+
resume_dispatch: false,
|
|
17
|
+
confirmed_stop: false,
|
|
18
|
+
files: false,
|
|
19
|
+
commands: false,
|
|
20
|
+
isolation: 'managed-process-no-coding-authority',
|
|
21
|
+
retention: 'durable-local',
|
|
22
|
+
} as const;
|
|
23
|
+
export const DIAGNOSTIC_PROFILE = {
|
|
24
|
+
schema_version: 1,
|
|
25
|
+
id: 'salaam/diagnostic',
|
|
26
|
+
revision: 1,
|
|
27
|
+
tools: ['salaam_probe'],
|
|
28
|
+
max_depth: 1,
|
|
29
|
+
} as const;
|
|
30
|
+
export function nativeCapabilities(
|
|
31
|
+
profile: AgentProfile,
|
|
32
|
+
coordination = false,
|
|
33
|
+
isolation?: WorkerBinding['isolation'],
|
|
34
|
+
) {
|
|
35
|
+
const caps = profile.role
|
|
36
|
+
? {
|
|
37
|
+
...NATIVE_CAPABILITIES,
|
|
38
|
+
repository_read: profile.tools.includes('salaam_repository'),
|
|
39
|
+
findings: profile.tools.includes('salaam_findings'),
|
|
40
|
+
}
|
|
41
|
+
: NATIVE_CAPABILITIES;
|
|
42
|
+
return {
|
|
43
|
+
...caps,
|
|
44
|
+
...(coordination ? { team_messages: true } : {}),
|
|
45
|
+
...(isolation
|
|
46
|
+
? {
|
|
47
|
+
files: profile.tools.includes('salaam_files'),
|
|
48
|
+
commands: profile.tools.includes('salaam_command'),
|
|
49
|
+
isolation:
|
|
50
|
+
isolation.execution.evidence === 'fixture'
|
|
51
|
+
? 'simulated-owned-copy'
|
|
52
|
+
: 'owned-copy-and-proven-executor',
|
|
53
|
+
execution_profile: isolation.execution.profile_digest,
|
|
54
|
+
execution_engine: isolation.execution.engine_id,
|
|
55
|
+
}
|
|
56
|
+
: {}),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function externalCapabilities(runtime: ExternalRuntime, tools: readonly string[]) {
|
|
61
|
+
return {
|
|
62
|
+
schema_version: 1 as const,
|
|
63
|
+
runtime: runtime.kind,
|
|
64
|
+
model_requests: false as const,
|
|
65
|
+
output_cap: runtime.output_cap,
|
|
66
|
+
files: runtime.files && tools.includes('salaam_files'),
|
|
67
|
+
commands: runtime.commands && tools.includes('salaam_command'),
|
|
68
|
+
model_usage: 'unknown' as const,
|
|
69
|
+
isolation:
|
|
70
|
+
runtime.kind === 'a2a'
|
|
71
|
+
? 'remote-peer-no-local-tool-authority'
|
|
72
|
+
: runtime.evidence === 'fixture'
|
|
73
|
+
? 'simulated-whole-runtime'
|
|
74
|
+
: 'proven-whole-runtime',
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export interface WorkerBinding {
|
|
78
|
+
schema_version: 1;
|
|
79
|
+
worker_id: string;
|
|
80
|
+
runtime: 'native-pi' | 'harness' | 'a2a';
|
|
81
|
+
owner_session: string;
|
|
82
|
+
owner_task: string | null;
|
|
83
|
+
parent_attempt: string;
|
|
84
|
+
step_key: string;
|
|
85
|
+
session_id: string;
|
|
86
|
+
attempt_id: string;
|
|
87
|
+
input_digest: string;
|
|
88
|
+
input_artifact: string | null;
|
|
89
|
+
profile: AgentProfile;
|
|
90
|
+
profile_digest: string;
|
|
91
|
+
capabilities: ReturnType<typeof nativeCapabilities> | ReturnType<typeof externalCapabilities>;
|
|
92
|
+
capability_digest: string;
|
|
93
|
+
tools: string[];
|
|
94
|
+
route: ProviderRoute | null;
|
|
95
|
+
workspace: { id: string; revision: number; digest: string } | null;
|
|
96
|
+
policy_digest: string;
|
|
97
|
+
bounds: { scope: 'model-requests'; requests: number } | { scope: 'invocations'; invocations: 1 };
|
|
98
|
+
external?: ExternalRuntime;
|
|
99
|
+
legacy: boolean;
|
|
100
|
+
agent?: AgentGrant;
|
|
101
|
+
repository?: { artifact_id: string; base_artifact: string; digest: string };
|
|
102
|
+
assignment?: { id: string; generation: number };
|
|
103
|
+
isolation?: {
|
|
104
|
+
workspace_id: string;
|
|
105
|
+
baseline_artifact: string;
|
|
106
|
+
digest: string;
|
|
107
|
+
execution: { profile_digest: string; engine_id: string; evidence: 'proven' | 'fixture' };
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
export interface WorkerBoundaryView {
|
|
111
|
+
binding: WorkerBinding;
|
|
112
|
+
binding_digest: string;
|
|
113
|
+
generation: number;
|
|
114
|
+
worker_epoch: number | null;
|
|
115
|
+
dispatch_state: 'intent' | 'dispatching' | 'accepted' | 'interrupted' | 'terminal';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Never equate one opaque invocation with one model request. Invocation admission is separate. */
|
|
119
|
+
export function assertModelRequestBoundary(capabilities: {
|
|
120
|
+
schema_version: number;
|
|
121
|
+
model_requests: boolean;
|
|
122
|
+
}): void {
|
|
123
|
+
if (capabilities.schema_version !== 1)
|
|
124
|
+
throw new ServiceError('worker_capability_version', 'Unsupported worker capability version');
|
|
125
|
+
if (capabilities.model_requests !== true)
|
|
126
|
+
throw new ServiceError(
|
|
127
|
+
'worker_budget_scope',
|
|
128
|
+
'Model-request bounds require enforced admission; opaque workers need explicit invocation bounds',
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export class WorkerBoundary {
|
|
133
|
+
profiles?: AgentProfiles;
|
|
134
|
+
assertAssignment?: (binding: WorkerBinding) => void;
|
|
135
|
+
assertIsolation?: (binding: WorkerBinding) => void;
|
|
136
|
+
constructor(private store: Store) {}
|
|
137
|
+
/** Called within the schema migration transaction. Cached responses and child rows stay intact. */
|
|
138
|
+
migrate(): void {
|
|
139
|
+
this.store.db.exec(`
|
|
140
|
+
CREATE TABLE worker_profiles(id TEXT NOT NULL,revision INTEGER NOT NULL,digest TEXT NOT NULL,document TEXT NOT NULL,revoked INTEGER NOT NULL DEFAULT 0,PRIMARY KEY(id,revision));
|
|
141
|
+
CREATE TABLE child_workers(child_id TEXT PRIMARY KEY REFERENCES child_tasks(id),binding TEXT NOT NULL,binding_digest TEXT NOT NULL,generation INTEGER NOT NULL DEFAULT 0,worker_epoch INTEGER,dispatch_state TEXT NOT NULL);
|
|
142
|
+
CREATE UNIQUE INDEX child_worker_identity ON child_workers(json_extract(binding,'$.worker_id'));
|
|
143
|
+
`);
|
|
144
|
+
for (const child of this.store.db
|
|
145
|
+
.prepare('SELECT * FROM child_tasks')
|
|
146
|
+
.all() as unknown as ChildTask[]) {
|
|
147
|
+
this.freeze(child, null, true);
|
|
148
|
+
this.store.db
|
|
149
|
+
.prepare('UPDATE child_workers SET dispatch_state=? WHERE child_id=?')
|
|
150
|
+
.run(child.state === 'accepted' ? 'interrupted' : 'terminal', child.id);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/** Caller owns the transaction that creates the child, its reservation and input artifact. */
|
|
154
|
+
freeze(
|
|
155
|
+
child: ChildTask,
|
|
156
|
+
inputArtifact: string | null,
|
|
157
|
+
legacy = false,
|
|
158
|
+
agent?: AgentGrant,
|
|
159
|
+
repository?: WorkerBinding['repository'],
|
|
160
|
+
assignment?: WorkerBinding['assignment'],
|
|
161
|
+
isolation?: WorkerBinding['isolation'],
|
|
162
|
+
external?: ExternalRuntime,
|
|
163
|
+
): void {
|
|
164
|
+
const admittedProfile = agent?.profile ?? DIAGNOSTIC_PROFILE;
|
|
165
|
+
const profileDigest = digest(admittedProfile);
|
|
166
|
+
this.store.db
|
|
167
|
+
.prepare(
|
|
168
|
+
'INSERT OR IGNORE INTO worker_profiles(id,revision,digest,document) VALUES (?,?,?,?)',
|
|
169
|
+
)
|
|
170
|
+
.run(admittedProfile.id, admittedProfile.revision, profileDigest, canonical(admittedProfile));
|
|
171
|
+
const profile = this.store.db
|
|
172
|
+
.prepare('SELECT digest,revoked FROM worker_profiles WHERE id=? AND revision=?')
|
|
173
|
+
.get(admittedProfile.id, admittedProfile.revision);
|
|
174
|
+
if (profile?.revoked || profile?.digest !== profileDigest)
|
|
175
|
+
throw new ServiceError(
|
|
176
|
+
'worker_profile_revoked',
|
|
177
|
+
'Diagnostic worker profile is revoked or changed',
|
|
178
|
+
);
|
|
179
|
+
const task = this.store.db
|
|
180
|
+
.prepare('SELECT id FROM durable_tasks WHERE session_id=?')
|
|
181
|
+
.get(child.parent_session);
|
|
182
|
+
const workspace = this.store.db
|
|
183
|
+
.prepare('SELECT id,revision,digest FROM workspaces WHERE session_id=?')
|
|
184
|
+
.get(child.parent_session);
|
|
185
|
+
const binding: WorkerBinding = {
|
|
186
|
+
schema_version: 1,
|
|
187
|
+
worker_id: randomUUID(),
|
|
188
|
+
runtime: external?.kind ?? 'native-pi',
|
|
189
|
+
owner_session: child.parent_session,
|
|
190
|
+
owner_task: task ? String(task.id) : null,
|
|
191
|
+
parent_attempt: child.parent_attempt,
|
|
192
|
+
step_key: child.step_key,
|
|
193
|
+
session_id: child.child_session,
|
|
194
|
+
attempt_id: child.child_attempt,
|
|
195
|
+
input_digest: child.input_digest,
|
|
196
|
+
input_artifact: inputArtifact,
|
|
197
|
+
profile: admittedProfile,
|
|
198
|
+
profile_digest: profileDigest,
|
|
199
|
+
capabilities: nativeCapabilities(admittedProfile, !!assignment, isolation),
|
|
200
|
+
capability_digest: digest(nativeCapabilities(admittedProfile, !!assignment, isolation)),
|
|
201
|
+
tools: JSON.parse(child.authority),
|
|
202
|
+
route: external
|
|
203
|
+
? null
|
|
204
|
+
: (agent?.route ??
|
|
205
|
+
this.store.providerRoute(child.child_session) ??
|
|
206
|
+
this.store.providerRoute(child.parent_session)),
|
|
207
|
+
workspace: workspace
|
|
208
|
+
? {
|
|
209
|
+
id: String(workspace.id),
|
|
210
|
+
revision: Number(workspace.revision),
|
|
211
|
+
digest: String(workspace.digest),
|
|
212
|
+
}
|
|
213
|
+
: null,
|
|
214
|
+
policy_digest: POLICY_DIGEST,
|
|
215
|
+
bounds: external
|
|
216
|
+
? { scope: 'invocations', invocations: 1 }
|
|
217
|
+
: { scope: 'model-requests', requests: child.budget },
|
|
218
|
+
...(external ? { external } : {}),
|
|
219
|
+
legacy,
|
|
220
|
+
...(agent ? { agent } : {}),
|
|
221
|
+
...(repository ? { repository } : {}),
|
|
222
|
+
...(assignment ? { assignment } : {}),
|
|
223
|
+
...(isolation ? { isolation } : {}),
|
|
224
|
+
};
|
|
225
|
+
if (external) {
|
|
226
|
+
binding.capabilities = externalCapabilities(external, binding.tools);
|
|
227
|
+
binding.capability_digest = digest(binding.capabilities);
|
|
228
|
+
} else assertModelRequestBoundary(binding.capabilities);
|
|
229
|
+
this.store.db
|
|
230
|
+
.prepare(
|
|
231
|
+
"INSERT INTO child_workers(child_id,binding,binding_digest,dispatch_state) VALUES (?,?,?,'intent')",
|
|
232
|
+
)
|
|
233
|
+
.run(child.id, canonical(binding), digest(binding));
|
|
234
|
+
// Migration has no event publication; the child itself remains the legacy receipt.
|
|
235
|
+
if (!legacy)
|
|
236
|
+
this.store.event(child.parent_session, 'child.worker_intent', {
|
|
237
|
+
child_id: child.id,
|
|
238
|
+
worker_id: binding.worker_id,
|
|
239
|
+
binding_digest: digest(binding),
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
view(childId: string): WorkerBoundaryView {
|
|
243
|
+
const row = this.store.db.prepare('SELECT * FROM child_workers WHERE child_id=?').get(childId);
|
|
244
|
+
if (!row) throw new ServiceError('worker_binding_missing', 'Child has no worker binding');
|
|
245
|
+
return {
|
|
246
|
+
binding: JSON.parse(String(row.binding)),
|
|
247
|
+
binding_digest: String(row.binding_digest),
|
|
248
|
+
generation: Number(row.generation),
|
|
249
|
+
worker_epoch: row.worker_epoch === null ? null : Number(row.worker_epoch),
|
|
250
|
+
dispatch_state: row.dispatch_state as WorkerBoundaryView['dispatch_state'],
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
private validate(child: ChildTask, route: ProviderRoute): WorkerBoundaryView {
|
|
254
|
+
const view = this.view(child.id),
|
|
255
|
+
b = view.binding;
|
|
256
|
+
if (b.agent) {
|
|
257
|
+
if (
|
|
258
|
+
!this.profiles ||
|
|
259
|
+
b.agent.owner_session !== child.parent_session ||
|
|
260
|
+
b.agent.profile_digest !== b.profile_digest ||
|
|
261
|
+
canonical(b.agent.route) !== canonical(b.route)
|
|
262
|
+
)
|
|
263
|
+
throw new ServiceError(
|
|
264
|
+
'worker_binding_changed',
|
|
265
|
+
'Specialist admission belongs to another owner or route',
|
|
266
|
+
);
|
|
267
|
+
this.profiles.validate(b.agent);
|
|
268
|
+
}
|
|
269
|
+
const profile = this.store.db
|
|
270
|
+
.prepare('SELECT * FROM worker_profiles WHERE id=? AND revision=?')
|
|
271
|
+
.get(b.profile.id, b.profile.revision);
|
|
272
|
+
if (b.tools.some((t) => ['salaam_files', 'salaam_command'].includes(t)) && !b.isolation)
|
|
273
|
+
throw new ServiceError(
|
|
274
|
+
'worker_binding_changed',
|
|
275
|
+
'Coding workers require isolated workspace ownership',
|
|
276
|
+
);
|
|
277
|
+
if (b.isolation) {
|
|
278
|
+
if (!this.assertIsolation)
|
|
279
|
+
throw new ServiceError('worker_binding_changed', 'Execution boundary is unavailable');
|
|
280
|
+
this.assertIsolation(b);
|
|
281
|
+
}
|
|
282
|
+
if (b.assignment) {
|
|
283
|
+
if (!this.assertAssignment)
|
|
284
|
+
throw new ServiceError('worker_binding_changed', 'Assignment owner is unavailable');
|
|
285
|
+
this.assertAssignment(b);
|
|
286
|
+
}
|
|
287
|
+
const task = this.store.db
|
|
288
|
+
.prepare('SELECT id FROM durable_tasks WHERE session_id=?')
|
|
289
|
+
.get(child.parent_session);
|
|
290
|
+
const input = this.store.db
|
|
291
|
+
.prepare(
|
|
292
|
+
'SELECT s.text,a.digest,a.session_id FROM submissions s JOIN artifacts a ON a.id=? WHERE s.attempt_id=?',
|
|
293
|
+
)
|
|
294
|
+
.get(b.input_artifact, child.child_attempt);
|
|
295
|
+
if (
|
|
296
|
+
b.schema_version !== 1 ||
|
|
297
|
+
digest(b) !== view.binding_digest ||
|
|
298
|
+
b.runtime !== 'native-pi' ||
|
|
299
|
+
b.owner_session !== child.parent_session ||
|
|
300
|
+
b.owner_task !== (task ? String(task.id) : null) ||
|
|
301
|
+
b.parent_attempt !== child.parent_attempt ||
|
|
302
|
+
b.session_id !== child.child_session ||
|
|
303
|
+
b.attempt_id !== child.child_attempt ||
|
|
304
|
+
b.step_key !== child.step_key ||
|
|
305
|
+
b.input_digest !== child.input_digest ||
|
|
306
|
+
!input ||
|
|
307
|
+
input.session_id !== child.child_session ||
|
|
308
|
+
input.digest !== textDigest(String(input.text)) ||
|
|
309
|
+
b.bounds.scope !== 'model-requests' ||
|
|
310
|
+
b.bounds.requests !== child.budget ||
|
|
311
|
+
canonical(b.tools) !== child.authority ||
|
|
312
|
+
b.tools.some(
|
|
313
|
+
(tool) => !b.profile.tools.includes(tool) && !(tool === 'salaam_team' && b.assignment),
|
|
314
|
+
) ||
|
|
315
|
+
b.policy_digest !== POLICY_DIGEST ||
|
|
316
|
+
canonical(b.route) !== canonical(route) ||
|
|
317
|
+
b.capability_digest !== digest(nativeCapabilities(b.profile, !!b.assignment, b.isolation)) ||
|
|
318
|
+
digest(b.capabilities) !== b.capability_digest ||
|
|
319
|
+
(!b.agent && b.profile_digest !== digest(DIAGNOSTIC_PROFILE)) ||
|
|
320
|
+
digest(b.profile) !== b.profile_digest ||
|
|
321
|
+
!profile ||
|
|
322
|
+
profile.revoked ||
|
|
323
|
+
profile.digest !== b.profile_digest ||
|
|
324
|
+
digest(JSON.parse(String(profile.document))) !== b.profile_digest
|
|
325
|
+
)
|
|
326
|
+
throw new ServiceError(
|
|
327
|
+
'worker_binding_changed',
|
|
328
|
+
'Frozen worker owner, profile, capabilities or configuration changed',
|
|
329
|
+
);
|
|
330
|
+
assertModelRequestBoundary(b.capabilities);
|
|
331
|
+
return view;
|
|
332
|
+
}
|
|
333
|
+
/** Commit dispatch intent before launching a process. It can be claimed only once. */
|
|
334
|
+
claim(child: ChildTask, epoch: number, route: ProviderRoute): void {
|
|
335
|
+
this.store.transaction(() => {
|
|
336
|
+
const view = this.validate(child, route);
|
|
337
|
+
if (
|
|
338
|
+
view.dispatch_state !== 'intent' ||
|
|
339
|
+
child.state !== 'accepted' ||
|
|
340
|
+
this.store.cancelled(child.child_attempt)
|
|
341
|
+
)
|
|
342
|
+
throw new ServiceError(
|
|
343
|
+
'worker_already_dispatched',
|
|
344
|
+
'Child worker dispatch cannot be replayed',
|
|
345
|
+
);
|
|
346
|
+
this.store.assertEpoch(child.child_session, epoch);
|
|
347
|
+
this.store.db
|
|
348
|
+
.prepare(
|
|
349
|
+
"UPDATE child_workers SET generation=generation+1,worker_epoch=?,dispatch_state='dispatching' WHERE child_id=?",
|
|
350
|
+
)
|
|
351
|
+
.run(epoch, child.id);
|
|
352
|
+
this.store.event(child.parent_session, 'child.worker_dispatching', {
|
|
353
|
+
child_id: child.id,
|
|
354
|
+
worker_id: view.binding.worker_id,
|
|
355
|
+
generation: view.generation + 1,
|
|
356
|
+
worker_epoch: epoch,
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
assert(child: ChildTask, epoch: number, route: ProviderRoute): WorkerBoundaryView {
|
|
361
|
+
const view = this.validate(child, route);
|
|
362
|
+
this.store.assertEpoch(child.child_session, epoch);
|
|
363
|
+
if (
|
|
364
|
+
view.generation !== 1 ||
|
|
365
|
+
view.worker_epoch !== epoch ||
|
|
366
|
+
!['dispatching', 'accepted'].includes(view.dispatch_state) ||
|
|
367
|
+
child.state !== 'accepted' ||
|
|
368
|
+
this.store.cancelled(child.child_attempt)
|
|
369
|
+
)
|
|
370
|
+
throw new ServiceError('stale_worker', 'Worker generation no longer owns this child');
|
|
371
|
+
return view;
|
|
372
|
+
}
|
|
373
|
+
accepted(child: ChildTask, epoch: number, route: ProviderRoute): void {
|
|
374
|
+
this.store.transaction(() => {
|
|
375
|
+
const view = this.assert(child, epoch, route);
|
|
376
|
+
if (view.dispatch_state !== 'dispatching')
|
|
377
|
+
throw new ServiceError('worker_already_dispatched', 'Duplicate worker acceptance');
|
|
378
|
+
this.store.db
|
|
379
|
+
.prepare("UPDATE child_workers SET dispatch_state='accepted' WHERE child_id=?")
|
|
380
|
+
.run(child.id);
|
|
381
|
+
this.store.event(child.parent_session, 'child.worker_accepted', {
|
|
382
|
+
child_id: child.id,
|
|
383
|
+
worker_id: view.binding.worker_id,
|
|
384
|
+
generation: view.generation,
|
|
385
|
+
worker_epoch: epoch,
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
/** Called with the terminal child result in the same transaction. */
|
|
390
|
+
finish(child: ChildTask, interrupted: boolean): void {
|
|
391
|
+
this.store.db
|
|
392
|
+
.prepare('UPDATE child_workers SET dispatch_state=? WHERE child_id=?')
|
|
393
|
+
.run(interrupted ? 'interrupted' : 'terminal', child.id);
|
|
394
|
+
}
|
|
395
|
+
}
|