@openscout/protocol 0.2.64 → 0.2.83
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/LICENSE +202 -0
- package/README.md +103 -52
- package/dist/a2a.d.ts +224 -0
- package/dist/a2a.d.ts.map +1 -0
- package/dist/a2a.js +225 -0
- package/dist/activity.d.ts +70 -0
- package/dist/activity.d.ts.map +1 -0
- package/dist/activity.js +1 -0
- package/dist/actors.d.ts +7 -3
- package/dist/actors.d.ts.map +1 -0
- package/dist/actors.js +15 -1
- package/dist/agent-broker-feed.d.ts +84 -0
- package/dist/agent-broker-feed.d.ts.map +1 -0
- package/dist/agent-broker-feed.js +1 -0
- package/dist/agent-identity.d.ts +23 -0
- package/dist/agent-identity.d.ts.map +1 -0
- package/dist/agent-identity.js +122 -8
- package/dist/agent-runs.d.ts +191 -0
- package/dist/agent-runs.d.ts.map +1 -0
- package/dist/agent-runs.js +365 -0
- package/dist/assigned-roles.d.ts +205 -0
- package/dist/assigned-roles.d.ts.map +1 -0
- package/dist/assigned-roles.js +228 -0
- package/dist/broker-routes.d.ts +40 -0
- package/dist/broker-routes.d.ts.map +1 -0
- package/dist/broker-routes.js +56 -0
- package/dist/budget.d.ts +65 -0
- package/dist/budget.d.ts.map +1 -0
- package/dist/budget.js +19 -0
- package/dist/capability-matrix.d.ts +230 -0
- package/dist/capability-matrix.d.ts.map +1 -0
- package/dist/capability-matrix.js +304 -0
- package/dist/channel-identity.d.ts +40 -0
- package/dist/channel-identity.d.ts.map +1 -0
- package/dist/channel-identity.js +108 -0
- package/dist/collaboration.d.ts +45 -16
- package/dist/collaboration.d.ts.map +1 -0
- package/dist/collaboration.js +39 -23
- package/dist/common.d.ts +4 -3
- package/dist/common.d.ts.map +1 -0
- package/dist/conversations.d.ts +1 -0
- package/dist/conversations.d.ts.map +1 -0
- package/dist/cursor-transport.d.ts +22 -0
- package/dist/cursor-transport.d.ts.map +1 -0
- package/dist/cursor-transport.js +10 -0
- package/dist/debug-attach.d.ts +29 -0
- package/dist/debug-attach.d.ts.map +1 -0
- package/dist/debug-attach.js +6 -0
- package/dist/deliveries.d.ts +16 -2
- package/dist/deliveries.d.ts.map +1 -0
- package/dist/durable-actions.d.ts +94 -0
- package/dist/durable-actions.d.ts.map +1 -0
- package/dist/durable-actions.js +1 -0
- package/dist/events.d.ts +27 -1
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +14 -1
- package/dist/harness-feature-support.d.ts +76 -0
- package/dist/harness-feature-support.d.ts.map +1 -0
- package/dist/harness-feature-support.js +29 -0
- package/dist/host-integrations.d.ts +22 -0
- package/dist/host-integrations.d.ts.map +1 -0
- package/dist/host-integrations.js +64 -0
- package/dist/inbox.d.ts +50 -0
- package/dist/inbox.d.ts.map +1 -0
- package/dist/inbox.js +9 -0
- package/dist/index.d.ts +35 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -2
- package/dist/invocations.d.ts +119 -4
- package/dist/invocations.d.ts.map +1 -0
- package/dist/invocations.js +201 -1
- package/dist/mesh.d.ts +98 -0
- package/dist/mesh.d.ts.map +1 -0
- package/dist/mesh.js +18 -1
- package/dist/messages.d.ts +3 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/native-interactions.d.ts +30 -0
- package/dist/native-interactions.d.ts.map +1 -0
- package/dist/native-interactions.js +51 -0
- package/dist/observed-status.d.ts +33 -0
- package/dist/observed-status.d.ts.map +1 -0
- package/dist/observed-status.js +6 -0
- package/dist/orchestrator-prompt.d.ts +10 -0
- package/dist/orchestrator-prompt.d.ts.map +1 -0
- package/dist/orchestrator-prompt.js +36 -0
- package/dist/permission-policy.d.ts +5 -0
- package/dist/permission-policy.d.ts.map +1 -0
- package/dist/permission-policy.js +15 -0
- package/dist/provisional-agent-names.d.ts +30 -0
- package/dist/provisional-agent-names.d.ts.map +1 -0
- package/dist/provisional-agent-names.js +224 -0
- package/dist/read-receipts.d.ts +12 -0
- package/dist/read-receipts.d.ts.map +1 -0
- package/dist/read-receipts.js +1 -0
- package/dist/relay-agent-card.d.ts +10 -0
- package/dist/relay-agent-card.d.ts.map +1 -0
- package/dist/rendezvous.d.ts +97 -0
- package/dist/rendezvous.d.ts.map +1 -0
- package/dist/rendezvous.js +35 -0
- package/dist/reserved-vocabulary.d.ts +21 -0
- package/dist/reserved-vocabulary.d.ts.map +1 -0
- package/dist/reserved-vocabulary.js +101 -0
- package/dist/route-aliases.d.ts +112 -0
- package/dist/route-aliases.d.ts.map +1 -0
- package/dist/route-aliases.js +1 -0
- package/dist/runtime-catalog-contract.d.ts +39 -0
- package/dist/runtime-catalog-contract.d.ts.map +1 -0
- package/dist/runtime-catalog-contract.js +17 -0
- package/dist/runtime-catalog.d.ts +11 -0
- package/dist/runtime-catalog.d.ts.map +1 -0
- package/dist/runtime-catalog.generated.d.ts +230 -0
- package/dist/runtime-catalog.generated.d.ts.map +1 -0
- package/dist/runtime-catalog.generated.js +299 -0
- package/dist/runtime-catalog.js +192 -0
- package/dist/runtime-execution.d.ts +161 -0
- package/dist/runtime-execution.d.ts.map +1 -0
- package/dist/runtime-execution.js +327 -0
- package/dist/runtime-profiles.d.ts +12 -0
- package/dist/runtime-profiles.d.ts.map +1 -0
- package/dist/runtime-profiles.js +20 -0
- package/dist/scout-agent-card.d.ts +10 -0
- package/dist/scout-agent-card.d.ts.map +1 -0
- package/dist/scout-composer.d.ts +27 -0
- package/dist/scout-composer.d.ts.map +1 -0
- package/dist/scout-composer.js +192 -0
- package/dist/scout-delivery.d.ts +99 -4
- package/dist/scout-delivery.d.ts.map +1 -0
- package/dist/scout-dispatch.d.ts +40 -3
- package/dist/scout-dispatch.d.ts.map +1 -0
- package/dist/scout-reply-context.d.ts +1 -0
- package/dist/scout-reply-context.d.ts.map +1 -0
- package/dist/session-display-state.d.ts +132 -0
- package/dist/session-display-state.d.ts.map +1 -0
- package/dist/session-display-state.js +94 -0
- package/dist/terminal-host-topology.d.ts +119 -0
- package/dist/terminal-host-topology.d.ts.map +1 -0
- package/dist/terminal-host-topology.js +21 -0
- package/dist/terminal-sessions.d.ts +142 -0
- package/dist/terminal-sessions.d.ts.map +1 -0
- package/dist/terminal-sessions.js +81 -0
- package/dist/terminal-surface-id.d.ts +73 -0
- package/dist/terminal-surface-id.d.ts.map +1 -0
- package/dist/terminal-surface-id.js +162 -0
- package/dist/terminal-workspaces.d.ts +199 -0
- package/dist/terminal-workspaces.d.ts.map +1 -0
- package/dist/terminal-workspaces.js +287 -0
- package/dist/thread-events.d.ts +1 -0
- package/dist/thread-events.d.ts.map +1 -0
- package/dist/time.d.ts +16 -0
- package/dist/time.d.ts.map +1 -0
- package/dist/time.js +33 -0
- package/dist/transports.d.ts +1 -0
- package/dist/transports.d.ts.map +1 -0
- package/package.json +37 -9
- package/src/a2a.ts +471 -0
- package/src/activity.ts +113 -0
- package/src/actors.ts +42 -8
- package/src/agent-broker-feed.ts +102 -0
- package/src/agent-identity.ts +143 -8
- package/src/agent-runs.ts +675 -0
- package/src/assigned-roles.ts +437 -0
- package/src/broker-routes.ts +62 -0
- package/src/budget.ts +88 -0
- package/src/capability-matrix.ts +648 -0
- package/src/channel-identity.ts +147 -0
- package/src/collaboration.ts +78 -48
- package/src/common.ts +24 -5
- package/src/cursor-transport.ts +34 -0
- package/src/debug-attach.ts +38 -0
- package/src/deliveries.ts +16 -1
- package/src/durable-actions.ts +125 -0
- package/src/events.ts +31 -0
- package/src/harness-feature-support.ts +116 -0
- package/src/host-integrations.ts +104 -0
- package/src/inbox.ts +68 -0
- package/src/index.ts +34 -2
- package/src/invocations.ts +351 -4
- package/src/mesh.ts +130 -0
- package/src/messages.ts +2 -0
- package/src/native-interactions.ts +100 -0
- package/src/observed-status.ts +90 -0
- package/src/orchestrator-prompt.ts +47 -0
- package/src/permission-policy.ts +21 -0
- package/src/provisional-agent-names.ts +272 -0
- package/src/read-receipts.ts +12 -0
- package/src/relay-agent-card.ts +73 -63
- package/src/rendezvous.ts +154 -0
- package/src/reserved-vocabulary.ts +135 -0
- package/src/route-aliases.ts +130 -0
- package/src/runtime-catalog-contract.ts +60 -0
- package/src/runtime-catalog.generated.ts +300 -0
- package/src/runtime-catalog.md +21 -0
- package/src/runtime-catalog.ts +193 -0
- package/src/runtime-catalog.v1.json +81 -0
- package/src/runtime-execution.ts +545 -0
- package/src/runtime-profiles.ts +37 -0
- package/src/scout-agent-card.ts +10 -0
- package/src/scout-composer.ts +249 -0
- package/src/scout-delivery.ts +113 -3
- package/src/scout-dispatch.ts +33 -2
- package/src/session-display-state.ts +202 -0
- package/src/terminal-host-topology.ts +132 -0
- package/src/terminal-sessions.ts +187 -0
- package/src/terminal-surface-id.ts +189 -0
- package/src/terminal-workspaces.ts +455 -0
- package/src/time.ts +49 -0
- package/dist/agent-address.d.ts +0 -1
- package/dist/agent-address.js +0 -1
- package/dist/agent-selectors.d.ts +0 -1
- package/dist/agent-selectors.js +0 -1
- package/src/agent-address.ts +0 -1
- package/src/agent-identity.test.ts +0 -435
- package/src/agent-selectors.ts +0 -1
- package/src/scout-agent-card.test.ts +0 -54
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { MetadataMap, ScoutId } from "./common.js";
|
|
2
|
+
|
|
3
|
+
export type ConversationIdentityRecord = {
|
|
4
|
+
id: ScoutId;
|
|
5
|
+
kind: string;
|
|
6
|
+
metadata?: MetadataMap;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const CHANNEL_ID_PREFIX = "chn-";
|
|
10
|
+
export const CHAT_ID_PREFIX = CHANNEL_ID_PREFIX;
|
|
11
|
+
export const LEGACY_CHAT_ID_PREFIX = "chat_";
|
|
12
|
+
export const LEGACY_CHANNEL_ID_PREFIX = "c.";
|
|
13
|
+
export const CHANNEL_NATURAL_KEY_METADATA = "naturalKey";
|
|
14
|
+
|
|
15
|
+
export function mintChannelId(randomUuid: () => string): ScoutId {
|
|
16
|
+
return `${CHAT_ID_PREFIX}${randomUuid().toLowerCase().replace(/-/g, "")}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Mint the opaque id for a named/system channel from its canonical identity.
|
|
21
|
+
* Every concurrent creator therefore arrives at the same broker record even
|
|
22
|
+
* when each caller began from a stale snapshot.
|
|
23
|
+
*/
|
|
24
|
+
export function stableChannelId(naturalKey: string): ScoutId {
|
|
25
|
+
const normalized = naturalKey.trim().toLowerCase();
|
|
26
|
+
const hash = (seed: bigint): string => {
|
|
27
|
+
let value = seed;
|
|
28
|
+
for (let index = 0; index < normalized.length; index += 1) {
|
|
29
|
+
value ^= BigInt(normalized.charCodeAt(index));
|
|
30
|
+
value = BigInt.asUintN(64, value * 0x100000001b3n);
|
|
31
|
+
}
|
|
32
|
+
return value.toString(16).padStart(16, "0");
|
|
33
|
+
};
|
|
34
|
+
return `${CHAT_ID_PREFIX}${hash(0xcbf29ce484222325n)}${hash(0x84222325cbf29ce4n)}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isOpaqueChannelId(value: string | null | undefined): value is ScoutId {
|
|
38
|
+
if (typeof value !== "string") return false;
|
|
39
|
+
return (
|
|
40
|
+
value.startsWith(CHAT_ID_PREFIX) && value.length > CHAT_ID_PREFIX.length
|
|
41
|
+
) || (
|
|
42
|
+
value.startsWith(LEGACY_CHAT_ID_PREFIX)
|
|
43
|
+
&& value.length > LEGACY_CHAT_ID_PREFIX.length
|
|
44
|
+
) || (
|
|
45
|
+
value.startsWith(LEGACY_CHANNEL_ID_PREFIX)
|
|
46
|
+
&& value.length > LEGACY_CHANNEL_ID_PREFIX.length
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function directChannelNaturalKey(participantIds: ScoutId[]): string {
|
|
51
|
+
return `direct:${stableIdentityParts(participantIds).join(",")}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function namedChannelNaturalKey(channel: string): string {
|
|
55
|
+
return `channel:${encodeIdentityPart(channel.trim().toLowerCase() || "shared")}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function systemChannelNaturalKey(name: string): string {
|
|
59
|
+
return `system:${encodeIdentityPart(name.trim().toLowerCase() || "system")}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function channelNaturalKeyFromMetadata(
|
|
63
|
+
metadata: MetadataMap | undefined,
|
|
64
|
+
): string | null {
|
|
65
|
+
const value = metadata?.[CHANNEL_NATURAL_KEY_METADATA];
|
|
66
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Read the semantic identity of a conversation, including the structural ids
|
|
71
|
+
* written by Scout versions that predate `metadata.naturalKey`.
|
|
72
|
+
*
|
|
73
|
+
* Structural ids remain read-only compatibility aliases. New writes must use
|
|
74
|
+
* an opaque id and persist the natural key in metadata.
|
|
75
|
+
*/
|
|
76
|
+
export function conversationNaturalKey(
|
|
77
|
+
conversation: ConversationIdentityRecord,
|
|
78
|
+
): string | null {
|
|
79
|
+
const explicit = channelNaturalKeyFromMetadata(conversation.metadata);
|
|
80
|
+
if (explicit) {
|
|
81
|
+
return explicit;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (conversation.kind !== "channel" && conversation.kind !== "system") {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const metadataChannel = conversation.metadata?.channel;
|
|
89
|
+
const legacyChannel = conversation.id.startsWith("channel.")
|
|
90
|
+
? conversation.id.slice("channel.".length)
|
|
91
|
+
: null;
|
|
92
|
+
const channel = typeof metadataChannel === "string" && metadataChannel.trim()
|
|
93
|
+
? metadataChannel.trim()
|
|
94
|
+
: legacyChannel?.trim() || null;
|
|
95
|
+
if (!channel) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return conversation.kind === "system"
|
|
100
|
+
? systemChannelNaturalKey(channel)
|
|
101
|
+
: namedChannelNaturalKey(channel);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Return every record for one semantic conversation in deterministic priority
|
|
106
|
+
* order: the stable named-channel id, another opaque id, then structural
|
|
107
|
+
* compatibility aliases. This prevents snapshot insertion order from choosing
|
|
108
|
+
* which chat receives a write.
|
|
109
|
+
*/
|
|
110
|
+
export function conversationsWithNaturalKey<T extends ConversationIdentityRecord>(
|
|
111
|
+
conversations: Iterable<T>,
|
|
112
|
+
naturalKey: string,
|
|
113
|
+
): T[] {
|
|
114
|
+
const normalizedNaturalKey = naturalKey.trim();
|
|
115
|
+
const stableId = normalizedNaturalKey.startsWith("channel:")
|
|
116
|
+
|| normalizedNaturalKey.startsWith("system:")
|
|
117
|
+
? stableChannelId(normalizedNaturalKey)
|
|
118
|
+
: null;
|
|
119
|
+
const priority = (conversation: T): number => {
|
|
120
|
+
if (stableId && conversation.id === stableId) return 0;
|
|
121
|
+
if (isOpaqueChannelId(conversation.id)) return 1;
|
|
122
|
+
return 2;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
return [...conversations]
|
|
126
|
+
.filter((conversation) => conversationNaturalKey(conversation) === normalizedNaturalKey)
|
|
127
|
+
.sort((left, right) => priority(left) - priority(right) || left.id.localeCompare(right.id));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function preferredConversationWithNaturalKey<T extends ConversationIdentityRecord>(
|
|
131
|
+
conversations: Iterable<T>,
|
|
132
|
+
naturalKey: string,
|
|
133
|
+
): T | undefined {
|
|
134
|
+
return conversationsWithNaturalKey(conversations, naturalKey)[0];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function stableIdentityParts(values: ScoutId[]): string[] {
|
|
138
|
+
return Array.from(new Set(values.map((value) => value.trim()).filter(Boolean)))
|
|
139
|
+
.sort()
|
|
140
|
+
.map(encodeIdentityPart);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function encodeIdentityPart(value: string): string {
|
|
144
|
+
return encodeURIComponent(value).replace(/[!'()*]/g, (char) =>
|
|
145
|
+
`%${char.charCodeAt(0).toString(16).toUpperCase()}`,
|
|
146
|
+
);
|
|
147
|
+
}
|
package/src/collaboration.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MetadataMap, ScoutId } from "./common.js";
|
|
2
2
|
|
|
3
|
-
export type CollaborationKind = "
|
|
3
|
+
export type CollaborationKind = "work_item" | "question";
|
|
4
4
|
|
|
5
5
|
export type CollaborationPriority = "low" | "normal" | "high" | "urgent";
|
|
6
6
|
|
|
@@ -10,12 +10,6 @@ export type CollaborationAcceptanceState =
|
|
|
10
10
|
| "accepted"
|
|
11
11
|
| "reopened";
|
|
12
12
|
|
|
13
|
-
export type QuestionState =
|
|
14
|
-
| "open"
|
|
15
|
-
| "answered"
|
|
16
|
-
| "closed"
|
|
17
|
-
| "declined";
|
|
18
|
-
|
|
19
13
|
export type WorkItemState =
|
|
20
14
|
| "open"
|
|
21
15
|
| "working"
|
|
@@ -24,6 +18,12 @@ export type WorkItemState =
|
|
|
24
18
|
| "done"
|
|
25
19
|
| "cancelled";
|
|
26
20
|
|
|
21
|
+
export type QuestionState =
|
|
22
|
+
| "open"
|
|
23
|
+
| "answered"
|
|
24
|
+
| "closed"
|
|
25
|
+
| "declined";
|
|
26
|
+
|
|
27
27
|
export type CollaborationRelationKind =
|
|
28
28
|
| "blocks"
|
|
29
29
|
| "spawns"
|
|
@@ -37,7 +37,7 @@ export interface CollaborationRelation {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export interface CollaborationWaitingOn {
|
|
40
|
-
kind: "actor" | "
|
|
40
|
+
kind: "actor" | "work_item" | "approval" | "artifact" | "condition";
|
|
41
41
|
label: string;
|
|
42
42
|
targetId?: ScoutId;
|
|
43
43
|
metadata?: MetadataMap;
|
|
@@ -69,17 +69,6 @@ export interface CollaborationRecordBase {
|
|
|
69
69
|
metadata?: MetadataMap;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
export interface QuestionRecord extends CollaborationRecordBase {
|
|
73
|
-
kind: "question";
|
|
74
|
-
state: QuestionState;
|
|
75
|
-
acceptanceState: CollaborationAcceptanceState;
|
|
76
|
-
askedById?: ScoutId;
|
|
77
|
-
askedOfId?: ScoutId;
|
|
78
|
-
answerMessageId?: ScoutId;
|
|
79
|
-
spawnedWorkItemId?: ScoutId;
|
|
80
|
-
closedAt?: number;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
72
|
export interface WorkItemRecord extends CollaborationRecordBase {
|
|
84
73
|
kind: "work_item";
|
|
85
74
|
state: WorkItemState;
|
|
@@ -92,12 +81,33 @@ export interface WorkItemRecord extends CollaborationRecordBase {
|
|
|
92
81
|
completedAt?: number;
|
|
93
82
|
}
|
|
94
83
|
|
|
95
|
-
|
|
84
|
+
/**
|
|
85
|
+
* A lightweight information-seeking interaction. A peer of {@link WorkItemRecord},
|
|
86
|
+
* not a point on the same severity ladder: a question can resolve directly, attach
|
|
87
|
+
* to a work item, or spawn one.
|
|
88
|
+
*
|
|
89
|
+
* Acceptance is modeled separately from workflow state (via `acceptanceState`) so a
|
|
90
|
+
* reply and satisfaction do not collapse into one transition — a question can be
|
|
91
|
+
* `answered` without yet being `closed`.
|
|
92
|
+
*/
|
|
93
|
+
export interface QuestionRecord extends CollaborationRecordBase {
|
|
94
|
+
kind: "question";
|
|
95
|
+
state: QuestionState;
|
|
96
|
+
acceptanceState: CollaborationAcceptanceState;
|
|
97
|
+
/** The actor being asked to answer. */
|
|
98
|
+
askedById?: ScoutId;
|
|
99
|
+
answeredById?: ScoutId;
|
|
100
|
+
/** The delivered answer text, present once `state` is `answered`. */
|
|
101
|
+
answer?: string;
|
|
102
|
+
answeredAt?: number;
|
|
103
|
+
closedAt?: number;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type CollaborationRecord = WorkItemRecord | QuestionRecord;
|
|
96
107
|
|
|
97
108
|
export type CollaborationEventKind =
|
|
98
109
|
| "created"
|
|
99
110
|
| "claimed"
|
|
100
|
-
| "answered"
|
|
101
111
|
| "accepted"
|
|
102
112
|
| "reopened"
|
|
103
113
|
| "waiting"
|
|
@@ -105,7 +115,7 @@ export type CollaborationEventKind =
|
|
|
105
115
|
| "handoff"
|
|
106
116
|
| "review_requested"
|
|
107
117
|
| "done"
|
|
108
|
-
| "
|
|
118
|
+
| "dismissed"
|
|
109
119
|
| "cancelled";
|
|
110
120
|
|
|
111
121
|
export interface CollaborationEvent {
|
|
@@ -119,20 +129,47 @@ export interface CollaborationEvent {
|
|
|
119
129
|
metadata?: MetadataMap;
|
|
120
130
|
}
|
|
121
131
|
|
|
132
|
+
export function isWorkItemTerminalState(state: WorkItemState): boolean {
|
|
133
|
+
return state === "done" || state === "cancelled";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* A question is terminal once it is `closed` or `declined`. `answered` is NOT terminal
|
|
138
|
+
* — acceptance is tracked separately, so an answered question may still await closure.
|
|
139
|
+
*/
|
|
122
140
|
export function isQuestionTerminalState(state: QuestionState): boolean {
|
|
123
141
|
return state === "closed" || state === "declined";
|
|
124
142
|
}
|
|
125
143
|
|
|
126
|
-
|
|
127
|
-
|
|
144
|
+
/** Terminal-state check across both collaboration kinds. */
|
|
145
|
+
export function isCollaborationTerminalState(record: CollaborationRecord): boolean {
|
|
146
|
+
return record.kind === "work_item"
|
|
147
|
+
? isWorkItemTerminalState(record.state)
|
|
148
|
+
: isQuestionTerminalState(record.state);
|
|
128
149
|
}
|
|
129
150
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
151
|
+
/** Narrow a {@link CollaborationRecord} to a {@link WorkItemRecord}. */
|
|
152
|
+
export function isWorkItem(record: CollaborationRecord): record is WorkItemRecord {
|
|
153
|
+
return record.kind === "work_item";
|
|
154
|
+
}
|
|
134
155
|
|
|
135
|
-
|
|
156
|
+
/** Narrow a {@link CollaborationRecord} to a {@link QuestionRecord}. */
|
|
157
|
+
export function isQuestion(record: CollaborationRecord): record is QuestionRecord {
|
|
158
|
+
return record.kind === "question";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The actor a collaboration record is directed at / awaiting acceptance from: the
|
|
163
|
+
* `requestedById` for a work item, the `askedById` for a question. Consumers that
|
|
164
|
+
* route or attribute a record by its requester should use this rather than reaching
|
|
165
|
+
* for a kind-specific field, so questions are handled instead of silently dropped.
|
|
166
|
+
*/
|
|
167
|
+
export function collaborationRequesterId(record: CollaborationRecord): ScoutId | undefined {
|
|
168
|
+
return isWorkItem(record) ? record.requestedById : record.askedById;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function collaborationRequiresNextMoveOwner(record: CollaborationRecord): boolean {
|
|
172
|
+
return !isCollaborationTerminalState(record);
|
|
136
173
|
}
|
|
137
174
|
|
|
138
175
|
export function collaborationRequiresOwner(record: CollaborationRecord): boolean {
|
|
@@ -148,11 +185,7 @@ export function collaborationRequiresAcceptance(record: CollaborationRecord): bo
|
|
|
148
185
|
return false;
|
|
149
186
|
}
|
|
150
187
|
|
|
151
|
-
|
|
152
|
-
return Boolean(record.askedById && record.askedOfId);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return Boolean(record.requestedById);
|
|
188
|
+
return Boolean(collaborationRequesterId(record));
|
|
156
189
|
}
|
|
157
190
|
|
|
158
191
|
export function validateCollaborationRecord(record: CollaborationRecord): string[] {
|
|
@@ -190,14 +223,18 @@ export function validateCollaborationRecord(record: CollaborationRecord): string
|
|
|
190
223
|
errors.push("waiting work items require waitingOn");
|
|
191
224
|
}
|
|
192
225
|
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
226
|
+
if (
|
|
227
|
+
record.kind === "work_item"
|
|
228
|
+
&& record.waitingOn?.targetId
|
|
229
|
+
&& record.waitingOn.targetId === record.id
|
|
230
|
+
) {
|
|
198
231
|
errors.push("waitingOn.targetId cannot reference the work item itself");
|
|
199
232
|
}
|
|
200
233
|
|
|
234
|
+
if (record.kind === "question" && record.state === "answered" && !record.answer?.trim()) {
|
|
235
|
+
errors.push("answered questions require an answer");
|
|
236
|
+
}
|
|
237
|
+
|
|
201
238
|
if (record.acceptanceState !== "none" && !collaborationRequiresAcceptance(record)) {
|
|
202
239
|
errors.push("acceptanceState requires the corresponding requester and reviewer identities");
|
|
203
240
|
}
|
|
@@ -239,20 +276,13 @@ export function validateCollaborationEvent(
|
|
|
239
276
|
}
|
|
240
277
|
}
|
|
241
278
|
|
|
242
|
-
if (event.kind === "answered" && event.recordKind !== "question") {
|
|
243
|
-
errors.push("answered events only apply to questions");
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (event.kind === "declined" && event.recordKind !== "question") {
|
|
247
|
-
errors.push("declined events only apply to questions");
|
|
248
|
-
}
|
|
249
|
-
|
|
250
279
|
if (
|
|
251
280
|
(event.kind === "waiting"
|
|
252
281
|
|| event.kind === "progressed"
|
|
253
282
|
|| event.kind === "review_requested"
|
|
254
283
|
|| event.kind === "done"
|
|
255
|
-
|| event.kind === "cancelled"
|
|
284
|
+
|| event.kind === "cancelled"
|
|
285
|
+
|| event.kind === "claimed")
|
|
256
286
|
&& event.recordKind !== "work_item"
|
|
257
287
|
) {
|
|
258
288
|
errors.push(`${event.kind} events only apply to work items`);
|
package/src/common.ts
CHANGED
|
@@ -4,26 +4,35 @@ export type ActorKind =
|
|
|
4
4
|
| "person"
|
|
5
5
|
| "helper"
|
|
6
6
|
| "agent"
|
|
7
|
+
| "session"
|
|
7
8
|
| "system"
|
|
8
9
|
| "bridge"
|
|
9
10
|
| "device";
|
|
10
11
|
|
|
11
12
|
export type AgentState =
|
|
13
|
+
| "registered"
|
|
14
|
+
| "attaching"
|
|
15
|
+
| "waking"
|
|
12
16
|
| "offline"
|
|
13
17
|
| "idle"
|
|
14
18
|
| "active"
|
|
15
|
-
| "waiting"
|
|
19
|
+
| "waiting"
|
|
20
|
+
| "working"
|
|
21
|
+
| "unreachable"
|
|
22
|
+
| "failed"
|
|
23
|
+
| "superseded"
|
|
24
|
+
| "stopped";
|
|
16
25
|
|
|
17
26
|
export type VisibilityScope = "private" | "workspace" | "public" | "system";
|
|
18
27
|
|
|
19
28
|
export type DeliveryStatus =
|
|
20
|
-
// Outbox lifecycle
|
|
29
|
+
// Outbox lifecycle
|
|
21
30
|
| "accepted" // local journal write done, awaiting forward
|
|
22
31
|
| "peer_acked" // peer broker journaled the envelope
|
|
23
32
|
| "running" // target agent claimed the flight
|
|
24
33
|
| "completed" // terminal success
|
|
25
|
-
| "deferred" // within retry window — see
|
|
26
|
-
| "failed" // terminal failure — see
|
|
34
|
+
| "deferred" // within retry window — see DeliveryMetadata.nextAttemptAt
|
|
35
|
+
| "failed" // terminal failure — see DeliveryMetadata.failureReason
|
|
27
36
|
| "cancelled"
|
|
28
37
|
// Legacy values kept for backwards-compatible journal replay
|
|
29
38
|
| "pending"
|
|
@@ -63,11 +72,21 @@ export type DeliveryTransport =
|
|
|
63
72
|
| "email"
|
|
64
73
|
| "tts"
|
|
65
74
|
| "native_voice"
|
|
75
|
+
| "claude_channel"
|
|
66
76
|
| "claude_stream_json"
|
|
67
77
|
| "codex_app_server"
|
|
68
78
|
| "codex_exec"
|
|
69
79
|
| "claude_resume"
|
|
70
|
-
| "
|
|
80
|
+
| "pi_rpc"
|
|
81
|
+
| "grok_acp"
|
|
82
|
+
| "kimi_acp"
|
|
83
|
+
| "cursor_acp"
|
|
84
|
+
| "opencode_acp"
|
|
85
|
+
| "tmux"
|
|
86
|
+
| "cursor_exec"
|
|
87
|
+
| "cursor_cli_text"
|
|
88
|
+
| "cursor_cli_stream_json"
|
|
89
|
+
| "cursor_sdk_local";
|
|
71
90
|
|
|
72
91
|
export type DeliveryReason =
|
|
73
92
|
| "conversation_visibility"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Cursor harness transport modes under evaluation.
|
|
3
|
+
* See docs/eng/sco-047-cursor-transport-spike.md.
|
|
4
|
+
*/
|
|
5
|
+
export const CURSOR_LOCAL_TRANSPORT_MODES = [
|
|
6
|
+
"cursor_cli_text",
|
|
7
|
+
"cursor_cli_stream_json",
|
|
8
|
+
"cursor_sdk_local",
|
|
9
|
+
"cursor_sdk_local_no_key",
|
|
10
|
+
] as const;
|
|
11
|
+
|
|
12
|
+
export type CursorLocalTransportMode = typeof CURSOR_LOCAL_TRANSPORT_MODES[number];
|
|
13
|
+
|
|
14
|
+
export type CursorTransportAuthSource =
|
|
15
|
+
| "none"
|
|
16
|
+
| "env"
|
|
17
|
+
| "cursor_api_key_file"
|
|
18
|
+
| "cli_flag"
|
|
19
|
+
| "sdk_option";
|
|
20
|
+
|
|
21
|
+
export type CursorTransportSpikeResult = {
|
|
22
|
+
mode: CursorLocalTransportMode;
|
|
23
|
+
ok: boolean;
|
|
24
|
+
durationMs: number;
|
|
25
|
+
authSource: CursorTransportAuthSource;
|
|
26
|
+
sessionId?: string;
|
|
27
|
+
agentId?: string;
|
|
28
|
+
runId?: string;
|
|
29
|
+
outputText?: string;
|
|
30
|
+
eventCount?: number;
|
|
31
|
+
errorCode?: string;
|
|
32
|
+
errorMessage?: string;
|
|
33
|
+
notes?: string[];
|
|
34
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ScoutId } from "./common.js";
|
|
2
|
+
|
|
3
|
+
export interface AgentEndpointDebugTransport {
|
|
4
|
+
kind: "tmux";
|
|
5
|
+
state: "ready" | "starting" | "missing" | "stale" | "error";
|
|
6
|
+
sessionName: string;
|
|
7
|
+
paneTarget?: string;
|
|
8
|
+
cwd?: string;
|
|
9
|
+
attachable: boolean;
|
|
10
|
+
multiAttach: boolean;
|
|
11
|
+
lastProbeAt: number;
|
|
12
|
+
lastAttachAt?: number;
|
|
13
|
+
lastDetachAt?: number;
|
|
14
|
+
activeClients?: number;
|
|
15
|
+
detail?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface DebugTmuxAttachPlan {
|
|
19
|
+
endpointId: ScoutId;
|
|
20
|
+
transport: "tmux";
|
|
21
|
+
sessionName: string;
|
|
22
|
+
paneTarget?: string;
|
|
23
|
+
command?: ["tmux", "attach-session", "-t", string];
|
|
24
|
+
terminalRelay?: {
|
|
25
|
+
backend: "tmux";
|
|
26
|
+
tmuxSession: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function isDebugTransportAttachable(
|
|
31
|
+
transport: Pick<AgentEndpointDebugTransport, "state" | "attachable"> | null | undefined,
|
|
32
|
+
): boolean {
|
|
33
|
+
return Boolean(transport?.attachable && transport.state === "ready");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function buildTmuxAttachCommand(sessionName: string): ["tmux", "attach-session", "-t", string] {
|
|
37
|
+
return ["tmux", "attach-session", "-t", sessionName];
|
|
38
|
+
}
|
package/src/deliveries.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
DeliveryFailureReason,
|
|
2
3
|
DeliveryPolicy,
|
|
3
4
|
DeliveryReason,
|
|
4
5
|
DeliveryStatus,
|
|
@@ -8,6 +9,20 @@ import type {
|
|
|
8
9
|
ScoutId,
|
|
9
10
|
} from "./common.js";
|
|
10
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Delivery metadata carries a small set of well-known keys alongside arbitrary
|
|
14
|
+
* extension data. The typed fields document the keys the runtime reads on the
|
|
15
|
+
* retry path; the {@link MetadataMap} index signature keeps the bag open for
|
|
16
|
+
* transport-specific extras. The wire/persistence shape is unchanged — these
|
|
17
|
+
* were always plain metadata keys, now they are typed.
|
|
18
|
+
*/
|
|
19
|
+
export interface DeliveryMetadata extends MetadataMap {
|
|
20
|
+
/** Epoch ms of the next retry attempt. Set while `status` is `"deferred"`. */
|
|
21
|
+
nextAttemptAt?: number;
|
|
22
|
+
/** Classified failure reason. Set when `status` is `"failed"`. */
|
|
23
|
+
failureReason?: DeliveryFailureReason;
|
|
24
|
+
}
|
|
25
|
+
|
|
11
26
|
export interface DeliveryTarget {
|
|
12
27
|
id: ScoutId;
|
|
13
28
|
kind: DeliveryTargetKind;
|
|
@@ -31,7 +46,7 @@ export interface DeliveryIntent {
|
|
|
31
46
|
bindingId?: ScoutId;
|
|
32
47
|
leaseOwner?: string;
|
|
33
48
|
leaseExpiresAt?: number;
|
|
34
|
-
metadata?:
|
|
49
|
+
metadata?: DeliveryMetadata;
|
|
35
50
|
}
|
|
36
51
|
|
|
37
52
|
export interface DeliveryAttempt {
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { MetadataMap, ScoutId } from "./common.js";
|
|
2
|
+
|
|
3
|
+
// First implementation slice. Add work_item/question/approval only after ask,
|
|
4
|
+
// message_delivery, and checkback semantics prove out against real paths.
|
|
5
|
+
export type DurableActionKind = "ask" | "message_delivery" | "checkback";
|
|
6
|
+
|
|
7
|
+
export type DurableCheckbackMode =
|
|
8
|
+
| "notify"
|
|
9
|
+
| "scoutbot"
|
|
10
|
+
| "agent"
|
|
11
|
+
| "routine";
|
|
12
|
+
|
|
13
|
+
export interface DurableCheckbackMetadata extends MetadataMap {
|
|
14
|
+
dueAt: number;
|
|
15
|
+
mode?: DurableCheckbackMode;
|
|
16
|
+
title?: string;
|
|
17
|
+
body?: string;
|
|
18
|
+
prompt?: string;
|
|
19
|
+
ownerId?: ScoutId;
|
|
20
|
+
conversationId?: ScoutId;
|
|
21
|
+
recordId?: ScoutId;
|
|
22
|
+
route?: unknown;
|
|
23
|
+
recurrence?: unknown;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type DurableActionState =
|
|
27
|
+
| "pending"
|
|
28
|
+
| "leased"
|
|
29
|
+
| "running"
|
|
30
|
+
| "waiting"
|
|
31
|
+
| "completed"
|
|
32
|
+
| "failed"
|
|
33
|
+
| "cancelled";
|
|
34
|
+
|
|
35
|
+
export type DurableAttemptState =
|
|
36
|
+
| "pending"
|
|
37
|
+
| "running"
|
|
38
|
+
| "completed"
|
|
39
|
+
| "failed"
|
|
40
|
+
| "cancelled";
|
|
41
|
+
|
|
42
|
+
export interface DurableAction {
|
|
43
|
+
id: ScoutId;
|
|
44
|
+
kind: DurableActionKind;
|
|
45
|
+
subjectId: ScoutId;
|
|
46
|
+
authorityCellId: ScoutId;
|
|
47
|
+
state: DurableActionState;
|
|
48
|
+
idempotencyKey?: string;
|
|
49
|
+
leaseOwner?: string;
|
|
50
|
+
leaseGeneration: number;
|
|
51
|
+
leaseExpiresAt?: number;
|
|
52
|
+
createdAt: number;
|
|
53
|
+
updatedAt: number;
|
|
54
|
+
metadata?: MetadataMap;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface DurableAttempt {
|
|
58
|
+
id: ScoutId;
|
|
59
|
+
actionId: ScoutId;
|
|
60
|
+
attempt: number;
|
|
61
|
+
state: DurableAttemptState;
|
|
62
|
+
leaseGeneration: number;
|
|
63
|
+
error?: string;
|
|
64
|
+
startedAt?: number;
|
|
65
|
+
completedAt?: number;
|
|
66
|
+
metadata?: MetadataMap;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface DurableCheckpoint {
|
|
70
|
+
actionId: ScoutId;
|
|
71
|
+
name: string;
|
|
72
|
+
payload?: unknown;
|
|
73
|
+
ownerAttemptId?: ScoutId;
|
|
74
|
+
leaseOwner?: string;
|
|
75
|
+
leaseGeneration?: number;
|
|
76
|
+
createdAt: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface DurableSignal {
|
|
80
|
+
actionId: ScoutId;
|
|
81
|
+
name: string;
|
|
82
|
+
payload?: unknown;
|
|
83
|
+
leaseOwner?: string;
|
|
84
|
+
leaseGeneration?: number;
|
|
85
|
+
emittedAt: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface DurableActionCreateInput {
|
|
89
|
+
id: ScoutId;
|
|
90
|
+
kind: DurableActionKind;
|
|
91
|
+
subjectId: ScoutId;
|
|
92
|
+
authorityCellId: ScoutId;
|
|
93
|
+
idempotencyKey?: string;
|
|
94
|
+
createdAt: number;
|
|
95
|
+
metadata?: MetadataMap;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface DurableActionClaimInput {
|
|
99
|
+
actionId: ScoutId;
|
|
100
|
+
owner: string;
|
|
101
|
+
leaseMs: number;
|
|
102
|
+
claimedAt: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface DurableActionHeartbeatInput {
|
|
106
|
+
actionId: ScoutId;
|
|
107
|
+
owner: string;
|
|
108
|
+
generation: number;
|
|
109
|
+
leaseMs: number;
|
|
110
|
+
heartbeatAt: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface DurableActionLease {
|
|
114
|
+
owner: string;
|
|
115
|
+
generation: number;
|
|
116
|
+
expiresAt: number;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface DurableActionCommandReceipt {
|
|
120
|
+
accepted: boolean;
|
|
121
|
+
duplicate?: boolean;
|
|
122
|
+
action: DurableAction;
|
|
123
|
+
lease?: DurableActionLease;
|
|
124
|
+
events: string[];
|
|
125
|
+
}
|