warpmetal 0.8.13 → 0.9.1
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/README.md +98 -4
- package/docs/agent-insights-cli.md +123 -0
- package/docs/agent-work-cli.md +72 -0
- package/docs/session-handoff-transport.md +68 -0
- package/package.json +5 -3
- package/skills/warpmetal/SKILL.md +54 -1
- package/skills/warpmetal/references/cli-reference.md +150 -2
- package/skills/warpmetal/references/runtime.md +19 -0
- package/src/account-gateway.js +217 -0
- package/src/account-session.js +209 -0
- package/src/agent-insights.js +638 -0
- package/src/agent-management.js +146 -0
- package/src/agent-work.js +800 -0
- package/src/api.js +10 -4
- package/src/cli.js +398 -11
- package/src/contracts/agent-manager-control-v1.schema.json +563 -0
- package/src/contracts/http.js +3 -0
- package/src/contracts/insights.js +133 -0
- package/src/contracts/manager.js +86 -0
- package/src/contracts/session-handoff.js +49 -0
- package/src/contracts/sources.json +12 -0
- package/src/contracts/work-handoff.js +73 -0
- package/src/contracts/work.js +310 -0
- package/src/customer-auth.js +329 -0
- package/src/installer.js +5 -5
- package/src/models.js +85 -0
- package/src/runtime.js +76 -3
- package/src/session-handoff.js +915 -0
- package/src/state.js +20 -4
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// Generated from worker/account/insights.ts; SHA-256 826eb08f9496415b408fe943d9eec33a6c8480a52474df76c118f471f6d42d1f.
|
|
2
|
+
/** Sanitized owner Insights projection and explicit metadata-only mutations. */
|
|
3
|
+
import { isPlainObject } from "./http.js";
|
|
4
|
+
export const FINDING_ID = /^finding_[A-Za-z0-9_-]{4,96}$/;
|
|
5
|
+
export const INSIGHTS_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
|
6
|
+
const RULES = ["repeated_identical_failure@1", "repeated_identical_call@1", "empty_result_loop@1", "suspected_stall@1"];
|
|
7
|
+
const GAPS = ["monitor_gap", "journal_generation_changed", "cursor_ahead", "source_unavailable", "unsupported_agent_version", "monitor_disabled", "export_unavailable", "capacity_exceeded"];
|
|
8
|
+
const PHASES = ["unknown", "idle", "running_model", "running_tool", "running_background", "waiting_permission", "waiting_input", "waiting_retry", "compacting"];
|
|
9
|
+
const SUGGESTIONS = ["inspect_first_failure", "check_repeated_operation", "refine_query", "inspect_active_phase"];
|
|
10
|
+
const RECOVERY = ["matching_operation_succeeded", "qualified_progress_resumed", "nonempty_refined_query", "qualified_progress_resumed"];
|
|
11
|
+
const id = (value, pattern = INSIGHTS_ID) => typeof value === "string" && pattern.test(value);
|
|
12
|
+
const positive = (value) => typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
13
|
+
const date = (value) => typeof value === "string" && value.length <= 40 && Number.isFinite(Date.parse(value));
|
|
14
|
+
const oneOf = (value, options) => typeof value === "string" && options.includes(value);
|
|
15
|
+
const pick = (value, keys) => Object.fromEntries(keys.map(key => [key, value[key]]));
|
|
16
|
+
const exact = (value, keys) => isPlainObject(value) && Object.keys(value).length === keys.length && keys.every(key => key in value);
|
|
17
|
+
export function insightsMutation(value, action = false) {
|
|
18
|
+
const keys = ["version", "requestId", "expectedRevision", ...(action ? ["action", "snoozeSeconds"] : ["enabled"])];
|
|
19
|
+
if (!exact(value, keys) || value.version !== 1 || !id(value.requestId) || !positive(value.expectedRevision))
|
|
20
|
+
return null;
|
|
21
|
+
if (!action)
|
|
22
|
+
return typeof value.enabled === "boolean" ? value : null;
|
|
23
|
+
if (value.action === "snooze")
|
|
24
|
+
return positive(value.snoozeSeconds) && value.snoozeSeconds >= 60 && value.snoozeSeconds <= 86400 ? value : null;
|
|
25
|
+
return oneOf(value.action, ["acknowledge", "dismiss"]) && value.snoozeSeconds === null ? value : null;
|
|
26
|
+
}
|
|
27
|
+
export function insightsQuery(params) {
|
|
28
|
+
const result = new URLSearchParams();
|
|
29
|
+
for (const key of params.keys()) {
|
|
30
|
+
if (!["limit", "cursor", "state", "session", "severity", "attention", "rule", "recentHours"].includes(key) || params.getAll(key).length !== 1)
|
|
31
|
+
return null;
|
|
32
|
+
const value = params.get(key);
|
|
33
|
+
if (key === "limit" ? !/^[1-9][0-9]{0,2}$/.test(value) || Number(value) > 100
|
|
34
|
+
: key === "cursor" ? !id(value, FINDING_ID) : key === "session" ? !id(value)
|
|
35
|
+
: key === "state" ? !["open", "resolved"].includes(value)
|
|
36
|
+
: key === "attention" ? !["unacknowledged", "acknowledged", "snoozed", "dismissed"].includes(value)
|
|
37
|
+
: key === "rule" ? !RULES.includes(value) : key === "recentHours" ? !["1", "24", "168", "720"].includes(value) : value !== "warning")
|
|
38
|
+
return null;
|
|
39
|
+
result.set(key, value);
|
|
40
|
+
}
|
|
41
|
+
return result.size ? `?${result}` : "";
|
|
42
|
+
}
|
|
43
|
+
export function projectInsightsSettings(value) {
|
|
44
|
+
if (!isPlainObject(value) || !isPlainObject(value.settings))
|
|
45
|
+
return null;
|
|
46
|
+
const s = value.settings;
|
|
47
|
+
if (s.version !== 1 || !positive(s.revision) || typeof s.enabled !== "boolean" ||
|
|
48
|
+
!(s.observedRevision === null || positive(s.observedRevision) && s.observedRevision <= s.revision) ||
|
|
49
|
+
!oneOf(s.status, ["pending", "ready", "degraded", "unsupported", "disabled", "unavailable"]) ||
|
|
50
|
+
!(s.lastObservedAt === null || date(s.lastObservedAt)) || !(s.gapReason === null || oneOf(s.gapReason, GAPS)) || s.retentionDays !== 30)
|
|
51
|
+
return null;
|
|
52
|
+
return { settings: pick(s, ["version", "revision", "enabled", "observedRevision", "status", "lastObservedAt", "gapReason", "retentionDays"]) };
|
|
53
|
+
}
|
|
54
|
+
export function projectInsight(value, findingId) {
|
|
55
|
+
if (!isPlainObject(value) || !id(value.findingId, FINDING_ID) || findingId !== undefined && value.findingId !== findingId ||
|
|
56
|
+
!oneOf(value.ruleId, RULES) || !oneOf(value.state, ["open", "resolved"]) || !positive(value.revision) ||
|
|
57
|
+
!positive(value.firstSequence) || !positive(value.lastSequence) || value.lastSequence < value.firstSequence ||
|
|
58
|
+
!positive(value.count) || !positive(value.threshold) || value.count < value.threshold || !date(value.firstObservedAt) ||
|
|
59
|
+
!date(value.lastObservedAt) || Date.parse(value.lastObservedAt) < Date.parse(value.firstObservedAt) ||
|
|
60
|
+
!oneOf(value.coverage, ["complete", "partial"]) || !oneOf(value.toolCategory, ["shell", "search", "file", "network", "other", "unknown"]) ||
|
|
61
|
+
!oneOf(value.phase, PHASES) || !positive(value.serviceGeneration) || !date(value.expiresAt) || value.severity !== "warning" ||
|
|
62
|
+
value.templateId !== value.ruleId || value.suggestionId !== SUGGESTIONS[RULES.indexOf(value.ruleId)] ||
|
|
63
|
+
!oneOf(value.sourceAvailability, ["available", "unavailable"]) ||
|
|
64
|
+
!(value.role === null || oneOf(value.role, ["manager", "worker", "reviewer"])) || !isPlainObject(value.attention))
|
|
65
|
+
return null;
|
|
66
|
+
for (const key of ["registeredSourceId", "workspaceEpoch", "nativeSessionId", "journalGeneration"])
|
|
67
|
+
if (!id(value[key]))
|
|
68
|
+
return null;
|
|
69
|
+
for (const key of ["phaseStartedAt", "lastProgressAt", "healthObservedAt"])
|
|
70
|
+
if (!(value[key] === null || date(value[key])))
|
|
71
|
+
return null;
|
|
72
|
+
if (!(value.stallThresholdMs === null || positive(value.stallThresholdMs)) || !Array.isArray(value.matchedCallIds) || value.matchedCallIds.length > 16 ||
|
|
73
|
+
value.matchedCallIds.some(ref => !id(ref)) || new Set(value.matchedCallIds).size !== value.matchedCallIds.length)
|
|
74
|
+
return null;
|
|
75
|
+
const a = value.attention;
|
|
76
|
+
if (!oneOf(a.state, ["unacknowledged", "acknowledged", "snoozed", "dismissed"]) || !positive(a.revision) || !(a.snoozedUntil === null || date(a.snoozedUntil)))
|
|
77
|
+
return null;
|
|
78
|
+
let recovery = null;
|
|
79
|
+
if (value.state === "resolved") {
|
|
80
|
+
const r = value.recovery;
|
|
81
|
+
if (!isPlainObject(r) || r.predicate !== RECOVERY[RULES.indexOf(value.ruleId)] || !positive(r.sequence) || r.sequence <= value.lastSequence || !id(r.referenceId))
|
|
82
|
+
return null;
|
|
83
|
+
recovery = pick(r, ["predicate", "sequence", "referenceId"]);
|
|
84
|
+
}
|
|
85
|
+
else if (value.recovery !== null)
|
|
86
|
+
return null;
|
|
87
|
+
return { ...pick(value, ["findingId", "ruleId", "state", "revision", "firstSequence", "lastSequence", "count", "threshold", "firstObservedAt", "lastObservedAt",
|
|
88
|
+
"coverage", "toolCategory", "phase", "phaseStartedAt", "lastProgressAt", "healthObservedAt", "stallThresholdMs", "registeredSourceId", "workspaceEpoch", "nativeSessionId",
|
|
89
|
+
"serviceGeneration", "journalGeneration", "severity", "templateId", "suggestionId", "sourceAvailability", "role", "expiresAt"]),
|
|
90
|
+
matchedCallIds: [...value.matchedCallIds], recovery, attention: pick(a, ["state", "revision", "snoozedUntil"]) };
|
|
91
|
+
}
|
|
92
|
+
export function projectInsightDetail(value, findingId) {
|
|
93
|
+
if (!isPlainObject(value))
|
|
94
|
+
return null;
|
|
95
|
+
const finding = projectInsight(value.finding, findingId);
|
|
96
|
+
return finding ? { finding } : null;
|
|
97
|
+
}
|
|
98
|
+
export function projectInsightsList(value) {
|
|
99
|
+
const settings = projectInsightsSettings(value);
|
|
100
|
+
if (!settings || !isPlainObject(value) || !Array.isArray(value.findings) || value.findings.length > 100 || !(value.nextCursor === null || id(value.nextCursor, FINDING_ID)))
|
|
101
|
+
return null;
|
|
102
|
+
const findings = value.findings.map(finding => projectInsight(finding));
|
|
103
|
+
if (findings.some(finding => finding === null) || new Set(findings.map(finding => finding.findingId)).size !== findings.length)
|
|
104
|
+
return null;
|
|
105
|
+
return { ...settings, findings, nextCursor: value.nextCursor };
|
|
106
|
+
}
|
|
107
|
+
export function projectInsightsSummary(value, serverId) {
|
|
108
|
+
if (!isPlainObject(value) || value.serverId !== serverId || !Array.isArray(value.boxes) || value.boxes.length > 100 ||
|
|
109
|
+
!(value.nextCursor === null || id(value.nextCursor, /^sbx_[A-Za-z0-9_-]{4,72}$/)))
|
|
110
|
+
return null;
|
|
111
|
+
const boxes = [];
|
|
112
|
+
for (const box of value.boxes) {
|
|
113
|
+
if (!isPlainObject(box) || !id(box.sandboxId, /^sbx_[A-Za-z0-9_-]{4,72}$/) ||
|
|
114
|
+
!Number.isSafeInteger(box.openFindings) || Number(box.openFindings) < 0 || Number(box.openFindings) > 1000 ||
|
|
115
|
+
!Number.isSafeInteger(box.attentionNeeded) || Number(box.attentionNeeded) < 0 || Number(box.attentionNeeded) > Number(box.openFindings))
|
|
116
|
+
return null;
|
|
117
|
+
const monitor = projectInsightsSettings({ settings: box.monitor });
|
|
118
|
+
if (!monitor)
|
|
119
|
+
return null;
|
|
120
|
+
let latest = null;
|
|
121
|
+
if (box.latestFinding !== null) {
|
|
122
|
+
const f = box.latestFinding;
|
|
123
|
+
if (!isPlainObject(f) || !id(f.findingId, FINDING_ID) || !oneOf(f.ruleId, RULES) || !positive(f.count) || !date(f.lastObservedAt) ||
|
|
124
|
+
!(f.role === null || oneOf(f.role, ["manager", "worker", "reviewer"])))
|
|
125
|
+
return null;
|
|
126
|
+
latest = pick(f, ["findingId", "ruleId", "count", "lastObservedAt", "role"]);
|
|
127
|
+
}
|
|
128
|
+
boxes.push({ sandboxId: box.sandboxId, monitor: monitor.settings, openFindings: box.openFindings, attentionNeeded: box.attentionNeeded, latestFinding: latest });
|
|
129
|
+
}
|
|
130
|
+
if (new Set(boxes.map(box => box.sandboxId)).size !== boxes.length)
|
|
131
|
+
return null;
|
|
132
|
+
return { serverId, boxes, nextCursor: value.nextCursor };
|
|
133
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Generated from worker/account/manager.ts; SHA-256 2f97f5d43411ef68208cd0a9b001416d535ae51e6b14cfd0a3164eb39d72a787.
|
|
2
|
+
/** The owner boundary accepts only the canonical closed, metadata-only schema. */
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
const contract = JSON.parse(readFileSync(new URL("./agent-manager-control-v1.schema.json", import.meta.url), "utf8"));
|
|
5
|
+
import { isPlainObject } from "./http.js";
|
|
6
|
+
export const MANAGER_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{3,159}$/;
|
|
7
|
+
const definitions = contract.$defs;
|
|
8
|
+
// This small interpreter is deliberately limited to the vocabulary in this
|
|
9
|
+
// versioned contract. Unsupported schema additions fail closed at the boundary.
|
|
10
|
+
const vocabulary = new Set(["$ref", "type", "const", "enum", "oneOf", "allOf", "properties", "required", "additionalProperties", "items", "maximum", "minimum", "maxItems", "minItems", "uniqueItems", "pattern", "format"]);
|
|
11
|
+
function matches(value, rule, depth = 0) {
|
|
12
|
+
if (!rule || depth > 32 || Object.keys(rule).some(key => !vocabulary.has(key)))
|
|
13
|
+
return false;
|
|
14
|
+
if (rule.$ref)
|
|
15
|
+
return rule.$ref.startsWith("#/$defs/") && matches(value, definitions[rule.$ref.slice(8)], depth + 1);
|
|
16
|
+
if ("const" in rule && value !== rule.const || rule.enum && !rule.enum.includes(value))
|
|
17
|
+
return false;
|
|
18
|
+
if (rule.oneOf && rule.oneOf.filter(r => matches(value, r, depth + 1)).length !== 1 || rule.allOf && !rule.allOf.every(r => matches(value, r, depth + 1)))
|
|
19
|
+
return false;
|
|
20
|
+
if (rule.type) {
|
|
21
|
+
const types = Array.isArray(rule.type) ? rule.type : [rule.type];
|
|
22
|
+
const type = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
|
|
23
|
+
if (!types.includes(type) && !(types.includes("integer") && typeof value === "number" && Number.isSafeInteger(value)))
|
|
24
|
+
return false;
|
|
25
|
+
if (type === "object" && !isPlainObject(value))
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (typeof value === "number" && (!Number.isSafeInteger(value) || rule.minimum !== undefined && value < rule.minimum || rule.maximum !== undefined && value > rule.maximum))
|
|
29
|
+
return false;
|
|
30
|
+
if (typeof value === "string" && (rule.pattern && !new RegExp(rule.pattern).test(value) || rule.format && (rule.format !== "date-time" || value.length > 40 || !/T.*(?:Z|[+-]\d\d:\d\d)$/.test(value) || !Number.isFinite(Date.parse(value)))))
|
|
31
|
+
return false;
|
|
32
|
+
if (Array.isArray(value)) {
|
|
33
|
+
if (rule.maxItems !== undefined && value.length > rule.maxItems || rule.minItems !== undefined && value.length < rule.minItems ||
|
|
34
|
+
rule.uniqueItems && new Set(value.map(v => JSON.stringify(v))).size !== value.length || rule.items && !value.every(v => matches(v, rule.items, depth + 1)))
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
if (isPlainObject(value)) {
|
|
38
|
+
if (rule.required?.some(key => !Object.hasOwn(value, key)) || rule.additionalProperties === false && Object.keys(value).some(key => !rule.properties || !Object.hasOwn(rule.properties, key)))
|
|
39
|
+
return false;
|
|
40
|
+
if (rule.properties && Object.entries(rule.properties).some(([key, child]) => Object.hasOwn(value, key) && !matches(value[key], child, depth + 1)))
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
export function projectManager(value, shape, scope = {}) {
|
|
46
|
+
if (!matches(value, definitions[shape]) || !isPlainObject(value))
|
|
47
|
+
return null;
|
|
48
|
+
if (shape === "activityList" || shape === "takeoverList") {
|
|
49
|
+
const rows = value[shape === "activityList" ? "runs" : "takeovers"];
|
|
50
|
+
const field = shape === "activityList" ? "runId" : "operationId";
|
|
51
|
+
if (new Set(rows.map(row => row[field])).size !== rows.length || rows.some(row => !projectManager(row, shape === "activityList" ? "activity" : "takeover", scope)))
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
else
|
|
55
|
+
for (const [key, wanted] of Object.entries(scope))
|
|
56
|
+
if (wanted !== undefined && value[key] !== wanted)
|
|
57
|
+
return null;
|
|
58
|
+
if (shape === "policy" && value.appliedRevision !== null && Number(value.appliedRevision) > Number(value.revision))
|
|
59
|
+
return null;
|
|
60
|
+
if (shape === "takeover") {
|
|
61
|
+
const receipt = value;
|
|
62
|
+
const policy = receipt.monitorPolicy, hold = receipt.memberHold;
|
|
63
|
+
if (policy.appliedRevision !== null && policy.appliedRevision > policy.desiredRevision ||
|
|
64
|
+
hold.nodeAppliedRevision !== null && hold.nodeAppliedRevision > hold.desiredRevision)
|
|
65
|
+
return null;
|
|
66
|
+
if (["ready", "resumed"].includes(receipt.state) && (policy.appliedRevision !== policy.desiredRevision ||
|
|
67
|
+
hold.nodeAppliedRevision !== hold.desiredRevision || !hold.brokerApplied ||
|
|
68
|
+
!["none_pending", "canceled", "already_available_to_worker"].includes(receipt.pendingInput.state) ||
|
|
69
|
+
(receipt.state === "ready" ? receipt.action !== "pause_manager_and_hold_member" || hold.desiredState !== "active"
|
|
70
|
+
: receipt.action !== "resume_manager_and_release_member" || hold.desiredState !== "released")))
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
return structuredClone(value);
|
|
74
|
+
}
|
|
75
|
+
export function managerMutation(value, shape) {
|
|
76
|
+
return matches(value, definitions[shape]) && isPlainObject(value) ? value : null;
|
|
77
|
+
}
|
|
78
|
+
export function managerListQuery(params, allowFinding = false) {
|
|
79
|
+
for (const key of params.keys()) {
|
|
80
|
+
const value = params.get(key);
|
|
81
|
+
if (!(allowFinding ? ["limit", "cursor", "findingId"] : ["limit", "cursor"]).includes(key) || params.getAll(key).length !== 1 ||
|
|
82
|
+
(key === "limit" ? !/^[1-9][0-9]{0,2}$/.test(value) || Number(value) > 100 : !MANAGER_ID.test(value)))
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
return params.size ? `?${params}` : "";
|
|
86
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Generated from worker/account/session-handoff.ts; SHA-256 d2bc3e8fc85cc686e4eba1fe539e18d5f1c8c2c832667b321b305fa0c8421396.
|
|
2
|
+
/** Closed safe metadata for the existing grant-bound managed bridge. */
|
|
3
|
+
import { isPlainObject } from "./http.js";
|
|
4
|
+
import { WORK_ID, WORK_BOX_ID } from "./work.js";
|
|
5
|
+
const ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
|
6
|
+
const DIGEST = /^sha256:[a-f0-9]{64}$/;
|
|
7
|
+
const positive = (v) => Number.isSafeInteger(v) && Number(v) > 0;
|
|
8
|
+
const id = (v, re = ID) => typeof v === "string" && re.test(v);
|
|
9
|
+
const closed = (v, keys) => isPlainObject(v) && Object.keys(v).length === keys.length && keys.every(k => k in v);
|
|
10
|
+
export function projectSessionHandoff(value, serverId, sandboxId, workId) {
|
|
11
|
+
if (!closed(value, ["formatVersion", "capability", "reason", "handoff", "access"]) || value.formatVersion !== 1 ||
|
|
12
|
+
!closed(value.access, ["transport", "requiresLocalOwnerCredential", "requiresSandboxGrant", "requiresPinnedHostKey"]) ||
|
|
13
|
+
value.access.transport !== "wm-team-control/1" || value.access.requiresLocalOwnerCredential !== true ||
|
|
14
|
+
value.access.requiresSandboxGrant !== true || value.access.requiresPinnedHostKey !== true)
|
|
15
|
+
return null;
|
|
16
|
+
if (value.capability === "unavailable")
|
|
17
|
+
return value.handoff === null && ["source_unavailable", "target_changed", "binding_not_verified", "managed_service_not_ready"].includes(String(value.reason)) ? value : null;
|
|
18
|
+
if (value.capability !== "exact_session" || value.reason !== null || !closed(value.handoff, ["formatVersion", "action", "handoffId", "issuedAt", "expiresAt", "identity", "source", "task", "work"]))
|
|
19
|
+
return null;
|
|
20
|
+
const h = value.handoff;
|
|
21
|
+
if (h.formatVersion !== 1 || h.action !== "open_session" || !id(h.handoffId) || typeof h.issuedAt !== "string" || typeof h.expiresAt !== "string")
|
|
22
|
+
return null;
|
|
23
|
+
const duration = Date.parse(h.expiresAt) - Date.parse(h.issuedAt);
|
|
24
|
+
if (!Number.isFinite(duration) || duration <= 0 || duration > 120000)
|
|
25
|
+
return null;
|
|
26
|
+
const i = h.identity, s = h.source;
|
|
27
|
+
if (!closed(i, ["serverId", "teamId", "memberId", "sandboxId", "sandboxGeneration", "serviceRegistrationId", "serviceGeneration", "instance", "role", "projectId", "workspaceEpoch", "profileId", "profileRevision", "profileDigest", "instructionRevision", "instructionDigest"]) ||
|
|
28
|
+
i.serverId !== serverId || i.sandboxId !== sandboxId || !WORK_BOX_ID.test(sandboxId) ||
|
|
29
|
+
!["worker", "reviewer", "manager"].includes(String(i.role)) || i.profileId !== "opencode" ||
|
|
30
|
+
!id(i.instance, /^[a-z0-9][a-z0-9_-]{0,31}$/) || !id(i.profileDigest, DIGEST) || !id(i.instructionDigest, DIGEST))
|
|
31
|
+
return null;
|
|
32
|
+
for (const field of ["serverId", "teamId", "memberId", "serviceRegistrationId", "projectId", "workspaceEpoch"])
|
|
33
|
+
if (!id(i[field]))
|
|
34
|
+
return null;
|
|
35
|
+
for (const field of ["sandboxGeneration", "serviceGeneration", "profileRevision", "instructionRevision"])
|
|
36
|
+
if (!positive(i[field]))
|
|
37
|
+
return null;
|
|
38
|
+
if (!closed(s, ["registeredSourceId", "nativeSessionId", "nativeProjectId", "nativeLocationDigest"]) || !id(s.registeredSourceId) ||
|
|
39
|
+
!id(s.nativeSessionId, /^ses_[A-Za-z0-9_-]{4,64}$/) || !id(s.nativeProjectId, /^(global|[a-f0-9]{40})$/) || !id(s.nativeLocationDigest, DIGEST))
|
|
40
|
+
return null;
|
|
41
|
+
if (h.task !== null && (!closed(h.task, ["taskId", "taskAttempt"]) || !id(h.task.taskId, /^task_[A-Za-z0-9_-]{4,75}$/) || !positive(h.task.taskAttempt)))
|
|
42
|
+
return null;
|
|
43
|
+
if (h.work !== null && (!closed(h.work, ["workId", "expectedRevision", "bindingId", "bindingRevision"]) || !id(h.work.workId, WORK_ID) ||
|
|
44
|
+
!id(h.work.bindingId, /^binding_[A-Za-z0-9_-]{4,72}$/) || !positive(h.work.expectedRevision) || !positive(h.work.bindingRevision)))
|
|
45
|
+
return null;
|
|
46
|
+
if (workId !== undefined && (!isPlainObject(h.work) || h.work.workId !== workId))
|
|
47
|
+
return null;
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Reviewed owner boundary adapters; mechanical TypeScript transpilation only",
|
|
3
|
+
"sources": {
|
|
4
|
+
"worker/account/work.ts": "e4176707a30848118bd44d1c369fc4303a267300fc981560d58c618b279ce516",
|
|
5
|
+
"worker/account/work-handoff.ts": "d89273eeab925968f757e6458542681d88356a494e6fc7d7af800193c371fb3d",
|
|
6
|
+
"worker/account/insights.ts": "826eb08f9496415b408fe943d9eec33a6c8480a52474df76c118f471f6d42d1f",
|
|
7
|
+
"worker/account/manager.ts": "2f97f5d43411ef68208cd0a9b001416d535ae51e6b14cfd0a3164eb39d72a787",
|
|
8
|
+
"worker/account/session-handoff.ts": "d2bc3e8fc85cc686e4eba1fe539e18d5f1c8c2c832667b321b305fa0c8421396",
|
|
9
|
+
"worker/account/http.ts#isPlainObject": "45a498e37caa3c89d198945a127cf050b9c11712c0a1ca050fac83a9b60c34b2",
|
|
10
|
+
"backend/contracts/agent-manager-control-v1.schema.json": "58ccce9f3324c13fb6cfa0cfe6d73fbbdb9d7222892541dc00e45547904ab4fb"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Generated from worker/account/work-handoff.ts; SHA-256 d89273eeab925968f757e6458542681d88356a494e6fc7d7af800193c371fb3d.
|
|
2
|
+
/** Explicit separate-session Work handoff; opaque owner receipts only. */
|
|
3
|
+
import { isPlainObject } from "./http.js";
|
|
4
|
+
import { WORK_ID, WORK_OPERATION_ID, WORK_REQUEST_ID, WORK_BOX_ID, workStateBody } from "./work.js";
|
|
5
|
+
const ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
|
6
|
+
const DIGEST = /^sha256:[a-f0-9]{64}$/;
|
|
7
|
+
const id = (value, pattern = ID) => typeof value === "string" && pattern.test(value);
|
|
8
|
+
const positive = (v) => Number.isSafeInteger(v) && Number(v) > 0;
|
|
9
|
+
const date = (v) => typeof v === "string" && v.length <= 40 && Number.isFinite(Date.parse(v));
|
|
10
|
+
const label = (v) => typeof v === "string" && v.trim() === v && v.length > 0 && new TextEncoder().encode(v).length <= 160 && !/[\x00-\x1f\x7f]/.test(v);
|
|
11
|
+
const pick = (v, keys) => Object.fromEntries(keys.map(k => [k, v[k]]));
|
|
12
|
+
export function workHandoffBody(value) {
|
|
13
|
+
if (!isPlainObject(value))
|
|
14
|
+
return null;
|
|
15
|
+
const extra = ["handoffKind", "restoreOperationId", "sessionChoice", "title"];
|
|
16
|
+
if (extra.some(k => !(k in value)) || value.sessionChoice !== "create_separate" || !label(value.title) ||
|
|
17
|
+
!(value.handoffKind === "reviewer" && value.restoreOperationId === null || value.handoffKind === "restored_target" && id(value.restoreOperationId, WORK_OPERATION_ID)))
|
|
18
|
+
return null;
|
|
19
|
+
const common = Object.fromEntries(Object.entries(value).filter(([k]) => !extra.includes(k)));
|
|
20
|
+
return workStateBody(common, "continue") ? value : null;
|
|
21
|
+
}
|
|
22
|
+
function member(value) {
|
|
23
|
+
if (!isPlainObject(value) || !id(value.memberId) || !label(value.label) || !["worker", "reviewer"].includes(String(value.role)) || !id(value.sandboxId, WORK_BOX_ID))
|
|
24
|
+
return null;
|
|
25
|
+
return pick(value, ["memberId", "label", "role", "sandboxId"]);
|
|
26
|
+
}
|
|
27
|
+
export function projectWorkHandoffTargets(value) {
|
|
28
|
+
if (!isPlainObject(value) || !Array.isArray(value.targets) || value.targets.length > 8 || value.nextCursor !== null || !Array.isArray(value.restores) || value.restores.length > 20 || typeof value.restoresTruncated !== "boolean")
|
|
29
|
+
return null;
|
|
30
|
+
const targets = value.targets.map(member);
|
|
31
|
+
if (targets.some(v => v === null) || new Set(targets.map(v => v.memberId)).size !== targets.length)
|
|
32
|
+
return null;
|
|
33
|
+
const restores = [];
|
|
34
|
+
for (const r of value.restores) {
|
|
35
|
+
if (!isPlainObject(r) || !id(r.operationId, WORK_OPERATION_ID) || !id(r.projectId) || !id(r.workspaceEpoch) || !id(r.sandboxId, WORK_BOX_ID))
|
|
36
|
+
return null;
|
|
37
|
+
restores.push(pick(r, ["operationId", "projectId", "workspaceEpoch", "sandboxId"]));
|
|
38
|
+
}
|
|
39
|
+
if (new Set(restores.map(r => r.operationId)).size !== restores.length)
|
|
40
|
+
return null;
|
|
41
|
+
return { targets: targets, nextCursor: null, restores, restoresTruncated: value.restoresTruncated };
|
|
42
|
+
}
|
|
43
|
+
export function projectWorkHandoff(value, workId, operationId, requestId) {
|
|
44
|
+
if (!isPlainObject(value) || !isPlainObject(value.operation))
|
|
45
|
+
return null;
|
|
46
|
+
const op = value.operation, target = member(op.targetMember);
|
|
47
|
+
if (!target || op.sourceWorkId !== workId || !id(op.operationId, WORK_OPERATION_ID) || !id(op.requestId, WORK_REQUEST_ID) ||
|
|
48
|
+
operationId !== undefined && op.operationId !== operationId || requestId !== undefined && op.requestId !== requestId ||
|
|
49
|
+
op.action !== "prepare_handoff" || op.sessionChoice !== "create_separate" || !["pending", "accepted", "failed", "superseded", "outcome_unknown"].includes(String(op.state)) ||
|
|
50
|
+
!positive(op.sourceRevision) || !id(op.targetWorkId, WORK_ID) || !id(op.checkpointOperationId, WORK_OPERATION_ID) ||
|
|
51
|
+
!(op.handoffKind === "reviewer" && op.restoreOperationId === null && target.role === "reviewer" || op.handoffKind === "restored_target" && id(op.restoreOperationId, WORK_OPERATION_ID)) ||
|
|
52
|
+
!date(op.createdAt) || !date(op.updatedAt) || ![op.baselineId, op.taskId, op.parentTaskId].every(v => v === null || id(v)) ||
|
|
53
|
+
!(op.errorCode === null || id(op.errorCode, /^[a-z][a-z0-9_]{0,79}$/)))
|
|
54
|
+
return null;
|
|
55
|
+
let workspace = null, session = null;
|
|
56
|
+
if (op.targetWorkspace !== null) {
|
|
57
|
+
const w = op.targetWorkspace;
|
|
58
|
+
if (!isPlainObject(w) || !id(w.selectionId) || !id(w.projectId) || !id(w.workspaceEpoch) || !positive(w.scopeRevision) || !id(w.rootAttestation, DIGEST))
|
|
59
|
+
return null;
|
|
60
|
+
workspace = pick(w, ["selectionId", "projectId", "workspaceEpoch", "scopeRevision", "rootAttestation"]);
|
|
61
|
+
}
|
|
62
|
+
if (op.session !== null) {
|
|
63
|
+
const s = op.session;
|
|
64
|
+
if (!isPlainObject(s) || !id(s.mappingId) || !id(s.registeredSourceId) || !id(s.nativeSessionId, /^ses_[A-Za-z0-9_-]{4,64}$/) ||
|
|
65
|
+
!id(s.nativeProjectId, /^(global|[a-f0-9]{40})$/) || !id(s.nativeLocationDigest, DIGEST))
|
|
66
|
+
return null;
|
|
67
|
+
session = pick(s, ["mappingId", "registeredSourceId", "nativeSessionId", "nativeProjectId", "nativeLocationDigest"]);
|
|
68
|
+
}
|
|
69
|
+
if (op.state === "accepted" && (!workspace || !session || !op.taskId || !op.baselineId))
|
|
70
|
+
return null;
|
|
71
|
+
return { operation: { ...pick(op, ["operationId", "requestId", "action", "handoffKind", "sessionChoice", "state", "sourceWorkId", "sourceRevision", "targetWorkId", "checkpointOperationId", "restoreOperationId", "baselineId", "taskId", "parentTaskId", "errorCode", "createdAt", "updatedAt"]),
|
|
72
|
+
targetMember: target, targetWorkspace: workspace, session } };
|
|
73
|
+
}
|