@borgee/agents-host 0.2.26 → 0.2.29

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.
@@ -6,10 +6,15 @@ export declare const CONTEXT_INJECTION_COMPATIBILITY_GATE = "context-injection";
6
6
  export declare const SKILL_RUNTIME_COMPATIBILITY_GATE = "skill-runtime";
7
7
  export declare const LOCALHOST_GATEWAY_COMPATIBILITY_GATE = "localhost-gateway";
8
8
  export declare const COLLABORATION_SKILL_FIRST_COMPATIBILITY_GATE = "collaboration-skill-first";
9
+ export declare const COLLABORATION_OUTCOME_MODEL_COMPATIBILITY_GATE = "collaboration-outcome-model";
10
+ export declare const ATTENTION_FOLLOW_SEMANTICS_COMPATIBILITY_GATE = "attention-follow-semantics";
11
+ export declare const TASK_THREAD_COLLABORATION_CONTRACT_COMPATIBILITY_GATE = "task-thread-collaboration-contract";
12
+ export declare const COLLABORATION_CAPABILITIES_DIAGNOSTICS_COMPATIBILITY_GATE = "collaboration-capabilities-diagnostics";
9
13
  export declare const TOKEN_BINDING_COMPATIBILITY_GATE = "token-binding";
10
14
  export declare const POLICY_AUDIT_ENFORCEMENT_COMPATIBILITY_GATE = "policy-audit-enforcement";
11
15
  export declare const MANAGED_RUNTIME_CONVERGENCE_COMPATIBILITY_GATE = "managed-runtime-convergence";
12
16
  export declare const COMPATIBILITY_GATES_ENV = "AGENTS_HOST_INTERNAL_COMPATIBILITY_GATES";
17
+ export declare const INTERNAL_DISABLED_COMPATIBILITY_GATES_ENV = "AGENTS_HOST_INTERNAL_DISABLED_COMPATIBILITY_GATES";
13
18
  export declare const INTERNAL_POLICY_MODE_ENV = "AGENTS_HOST_INTERNAL_POLICY_MODE";
14
19
  export declare const INTERNAL_PROVIDER_IMPLEMENTATIONS_ENV = "AGENTS_HOST_INTERNAL_PROVIDER_IMPLEMENTATIONS";
15
20
  export declare const MANAGED_RUNTIME_SETTINGS_SCHEMA_VERSION = 1;
@@ -23,8 +28,10 @@ export interface ManagedRuntimeSettingsSnapshot {
23
28
  providerImplementationOverrides: string[];
24
29
  internalPolicyMode: InternalPolicyMode;
25
30
  }
31
+ export declare const DEFAULT_INTERNAL_COMPATIBILITY_GATES: readonly string[];
26
32
  export declare function parseCompatibilityGates(rawValue: string | undefined): ReadonlySet<string>;
27
- export declare function resolveInternalCompatibilityGates(): ReadonlySet<string>;
33
+ export declare function resolveInternalCompatibilityGates(env?: NodeJS.ProcessEnv): ReadonlySet<string>;
34
+ export declare function resolveDisabledDefaultCompatibilityGates(compatibilityGates: Iterable<string>): string[];
28
35
  export declare function normalizeInternalProviderImplementationOverrides(rawValue: string | undefined): string[];
29
36
  export declare function parseInternalProviderImplementationOverrides(rawValue: string | undefined): InternalProviderImplementationOverrides;
30
37
  export declare function resolveManagedRuntimeProviderImplementationOverrides(env?: NodeJS.ProcessEnv, compatibilityGates?: string[]): string[];
@@ -7,14 +7,34 @@ export const CONTEXT_INJECTION_COMPATIBILITY_GATE = 'context-injection';
7
7
  export const SKILL_RUNTIME_COMPATIBILITY_GATE = 'skill-runtime';
8
8
  export const LOCALHOST_GATEWAY_COMPATIBILITY_GATE = 'localhost-gateway';
9
9
  export const COLLABORATION_SKILL_FIRST_COMPATIBILITY_GATE = 'collaboration-skill-first';
10
+ export const COLLABORATION_OUTCOME_MODEL_COMPATIBILITY_GATE = 'collaboration-outcome-model';
11
+ export const ATTENTION_FOLLOW_SEMANTICS_COMPATIBILITY_GATE = 'attention-follow-semantics';
12
+ export const TASK_THREAD_COLLABORATION_CONTRACT_COMPATIBILITY_GATE = 'task-thread-collaboration-contract';
13
+ export const COLLABORATION_CAPABILITIES_DIAGNOSTICS_COMPATIBILITY_GATE = 'collaboration-capabilities-diagnostics';
10
14
  export const TOKEN_BINDING_COMPATIBILITY_GATE = 'token-binding';
11
15
  export const POLICY_AUDIT_ENFORCEMENT_COMPATIBILITY_GATE = 'policy-audit-enforcement';
12
16
  export const MANAGED_RUNTIME_CONVERGENCE_COMPATIBILITY_GATE = 'managed-runtime-convergence';
13
17
  export const COMPATIBILITY_GATES_ENV = 'AGENTS_HOST_INTERNAL_COMPATIBILITY_GATES';
18
+ export const INTERNAL_DISABLED_COMPATIBILITY_GATES_ENV = 'AGENTS_HOST_INTERNAL_DISABLED_COMPATIBILITY_GATES';
14
19
  export const INTERNAL_POLICY_MODE_ENV = 'AGENTS_HOST_INTERNAL_POLICY_MODE';
15
20
  export const INTERNAL_PROVIDER_IMPLEMENTATIONS_ENV = 'AGENTS_HOST_INTERNAL_PROVIDER_IMPLEMENTATIONS';
16
21
  export const MANAGED_RUNTIME_SETTINGS_SCHEMA_VERSION = 1;
17
22
  const VALID_PROVIDER_IMPLEMENTATION_ENTRIES = '"claude:v1", "claude:v2", "copilot:v1", or "copilot:v2"';
23
+ export const DEFAULT_INTERNAL_COMPATIBILITY_GATES = Object.freeze([
24
+ CLAUDE_PROVIDER_V2_COMPATIBILITY_GATE,
25
+ COLLABORATION_CAPABILITIES_DIAGNOSTICS_COMPATIBILITY_GATE,
26
+ COLLABORATION_OUTCOME_MODEL_COMPATIBILITY_GATE,
27
+ CONNECTIONS_STATE_LAYER_COMPATIBILITY_GATE,
28
+ CONTEXT_INJECTION_COMPATIBILITY_GATE,
29
+ CODEX_PROVIDER_COMPATIBILITY_GATE,
30
+ COPILOT_PROVIDER_V2_COMPATIBILITY_GATE,
31
+ LOCALHOST_GATEWAY_COMPATIBILITY_GATE,
32
+ MANAGED_RUNTIME_CONVERGENCE_COMPATIBILITY_GATE,
33
+ POLICY_AUDIT_ENFORCEMENT_COMPATIBILITY_GATE,
34
+ SKILL_RUNTIME_COMPATIBILITY_GATE,
35
+ TASK_THREAD_COLLABORATION_CONTRACT_COMPATIBILITY_GATE,
36
+ TOKEN_BINDING_COMPATIBILITY_GATE,
37
+ ]);
18
38
  const PROVIDER_IMPLEMENTATION_COMPATIBILITY_GATES = {
19
39
  claude: CLAUDE_PROVIDER_V2_COMPATIBILITY_GATE,
20
40
  copilot: COPILOT_PROVIDER_V2_COMPATIBILITY_GATE,
@@ -46,8 +66,19 @@ export function parseCompatibilityGates(rawValue) {
46
66
  }
47
67
  return gates;
48
68
  }
49
- export function resolveInternalCompatibilityGates() {
50
- return parseCompatibilityGates(process.env[COMPATIBILITY_GATES_ENV]);
69
+ export function resolveInternalCompatibilityGates(env = process.env) {
70
+ const compatibilityGates = new Set(DEFAULT_INTERNAL_COMPATIBILITY_GATES);
71
+ for (const gate of parseCompatibilityGates(env[COMPATIBILITY_GATES_ENV])) {
72
+ compatibilityGates.add(gate);
73
+ }
74
+ for (const gate of parseCompatibilityGates(env[INTERNAL_DISABLED_COMPATIBILITY_GATES_ENV])) {
75
+ compatibilityGates.delete(gate);
76
+ }
77
+ return compatibilityGates;
78
+ }
79
+ export function resolveDisabledDefaultCompatibilityGates(compatibilityGates) {
80
+ const enabledGates = new Set(compatibilityGates);
81
+ return DEFAULT_INTERNAL_COMPATIBILITY_GATES.filter((gate) => !enabledGates.has(gate));
51
82
  }
52
83
  export function normalizeInternalProviderImplementationOverrides(rawValue) {
53
84
  if (!rawValue || rawValue.trim().length === 0) {
@@ -91,11 +122,20 @@ export function resolveManagedRuntimeProviderImplementationOverrides(env = proce
91
122
  });
92
123
  }
93
124
  export function resolveManagedRuntimeCompatibilityGates(env = process.env) {
94
- const compatibilityGates = new Set(parseCompatibilityGates(env[COMPATIBILITY_GATES_ENV]));
125
+ const compatibilityGates = new Set(resolveInternalCompatibilityGates(env));
126
+ const explicitlyEnabledGates = parseCompatibilityGates(env[COMPATIBILITY_GATES_ENV]);
95
127
  if (compatibilityGates.has(MANAGED_RUNTIME_CONVERGENCE_COMPATIBILITY_GATE)) {
96
128
  compatibilityGates.add(CONNECTIONS_STATE_LAYER_COMPATIBILITY_GATE);
97
129
  compatibilityGates.add(TOKEN_BINDING_COMPATIBILITY_GATE);
98
130
  }
131
+ else {
132
+ if (!explicitlyEnabledGates.has(CONNECTIONS_STATE_LAYER_COMPATIBILITY_GATE)) {
133
+ compatibilityGates.delete(CONNECTIONS_STATE_LAYER_COMPATIBILITY_GATE);
134
+ }
135
+ if (!explicitlyEnabledGates.has(TOKEN_BINDING_COMPATIBILITY_GATE)) {
136
+ compatibilityGates.delete(TOKEN_BINDING_COMPATIBILITY_GATE);
137
+ }
138
+ }
99
139
  return sortUniqueStrings(compatibilityGates);
100
140
  }
101
141
  export function resolveInternalPolicyMode(gateEnabled, env = process.env) {
package/dist/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { resolveSingleAgentStateRoot } from './state-paths.js';
2
- import { CODEX_PROVIDER_COMPATIBILITY_GATE, COMPATIBILITY_GATES_ENV, parseCompatibilityGates, } from './compatibility-gates.js';
2
+ import { CODEX_PROVIDER_COMPATIBILITY_GATE, resolveInternalCompatibilityGates, } from './compatibility-gates.js';
3
3
  const MAX_TIMER_DELAY_MS = 2_147_483_647;
4
4
  export const MAX_COPILOT_SESSION_TTL_MINUTES = MAX_TIMER_DELAY_MS / 60_000;
5
5
  export const DEFAULT_COPILOT_SESSION_TTL_MINUTES = 2 * 24 * 60;
@@ -69,9 +69,9 @@ export function assertProviderCompatibility(provider, sourceLabel, env = process
69
69
  if (provider !== 'codex') {
70
70
  return;
71
71
  }
72
- const compatibilityGates = parseCompatibilityGates(env[COMPATIBILITY_GATES_ENV]);
72
+ const compatibilityGates = resolveInternalCompatibilityGates(env);
73
73
  if (!compatibilityGates.has(CODEX_PROVIDER_COMPATIBILITY_GATE)) {
74
- throw new Error(`${sourceLabel}: provider "codex" requires ${COMPATIBILITY_GATES_ENV} to include ${CODEX_PROVIDER_COMPATIBILITY_GATE}`);
74
+ throw new Error(`${sourceLabel}: provider "codex" requires the shipped ${CODEX_PROVIDER_COMPATIBILITY_GATE} path to remain enabled`);
75
75
  }
76
76
  }
77
77
  export function resolveProviderCommandConfig(overrides = {}) {
@@ -0,0 +1,12 @@
1
+ import type { AttentionClaimActivation, AttentionClaimContext, AttentionClaimState, AttentionDeliveryMode, AttentionSnapshot, AttentionWakeMode } from '../types.js';
2
+ export declare function deriveAttentionWakeMode(snapshot: Pick<AttentionSnapshot, 'deliveryMode' | 'claimState' | 'claimActivation'>): AttentionWakeMode;
3
+ export declare function buildNormalizedAttentionSnapshot(params?: {
4
+ deliveryMode?: AttentionDeliveryMode;
5
+ claimState?: AttentionClaimState;
6
+ claimActivation?: AttentionClaimActivation;
7
+ observedAt?: number;
8
+ turnExecutionId?: string;
9
+ claimContext?: AttentionClaimContext;
10
+ }): AttentionSnapshot;
11
+ export declare function parsePersistedAttentionSnapshot(raw: unknown): AttentionSnapshot;
12
+ export declare function buildAttentionSummaryLines(snapshot: AttentionSnapshot | undefined): string[];
@@ -0,0 +1,137 @@
1
+ function isRecord(value) {
2
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
3
+ }
4
+ function isDeliveryMode(value) {
5
+ return value === 'default' || value === 'follow' || value === 'muted';
6
+ }
7
+ function isClaimState(value) {
8
+ return value === 'unclaimed' || value === 'claimed';
9
+ }
10
+ function isClaimActivation(value) {
11
+ return value === 'inactive' || value === 'active' || value === 'dormant';
12
+ }
13
+ function isUsableTaskId(value) {
14
+ return value.length > 0 && !/[\u0000-\u001f\u007f\s]/u.test(value);
15
+ }
16
+ function normalizeClaimContext(value) {
17
+ if (!isRecord(value) || typeof value.scope !== 'string' || typeof value.taskId !== 'string') {
18
+ return undefined;
19
+ }
20
+ const taskId = value.taskId.trim();
21
+ if (value.scope === 'task-thread') {
22
+ return isUsableTaskId(taskId) ? { scope: 'task-thread', taskId } : undefined;
23
+ }
24
+ if (value.scope === 'channel') {
25
+ return isUsableTaskId(taskId) ? { scope: 'channel', taskId } : undefined;
26
+ }
27
+ return undefined;
28
+ }
29
+ export function deriveAttentionWakeMode(snapshot) {
30
+ if (snapshot.deliveryMode === 'muted') {
31
+ return 'mute-non-mention';
32
+ }
33
+ if (snapshot.deliveryMode === 'follow') {
34
+ return 'follow-visible-human';
35
+ }
36
+ if (snapshot.claimState === 'claimed' && snapshot.claimActivation === 'active') {
37
+ return 'follow-visible-human';
38
+ }
39
+ return 'current-policy';
40
+ }
41
+ export function buildNormalizedAttentionSnapshot(params) {
42
+ let deliveryMode = params?.deliveryMode ?? 'default';
43
+ let claimState = params?.claimState ?? 'unclaimed';
44
+ let claimActivation = params?.claimActivation ?? 'inactive';
45
+ let claimContext = params?.claimContext;
46
+ if (claimState === 'unclaimed') {
47
+ claimActivation = 'inactive';
48
+ claimContext = undefined;
49
+ }
50
+ else if (claimActivation === 'inactive') {
51
+ claimState = 'unclaimed';
52
+ claimContext = undefined;
53
+ }
54
+ if (claimActivation === 'dormant') {
55
+ deliveryMode = 'default';
56
+ }
57
+ if (claimContext?.scope === 'task-thread' && !isUsableTaskId(claimContext.taskId)) {
58
+ claimState = 'unclaimed';
59
+ claimActivation = 'inactive';
60
+ claimContext = undefined;
61
+ deliveryMode = 'default';
62
+ }
63
+ const snapshot = {
64
+ source: 'host-observed',
65
+ deliveryMode,
66
+ claimState,
67
+ claimActivation,
68
+ wakeMode: deriveAttentionWakeMode({
69
+ deliveryMode,
70
+ claimState,
71
+ claimActivation,
72
+ }),
73
+ observedAt: params?.observedAt ?? Date.now(),
74
+ ...(params?.turnExecutionId ? { turnExecutionId: params.turnExecutionId } : {}),
75
+ ...(claimContext ? { claimContext } : {}),
76
+ };
77
+ return snapshot;
78
+ }
79
+ export function parsePersistedAttentionSnapshot(raw) {
80
+ if (!isRecord(raw)) {
81
+ return buildNormalizedAttentionSnapshot();
82
+ }
83
+ const deliveryMode = isDeliveryMode(raw.deliveryMode) ? raw.deliveryMode : 'default';
84
+ const claimState = isClaimState(raw.claimState) ? raw.claimState : 'unclaimed';
85
+ const claimActivation = isClaimActivation(raw.claimActivation) ? raw.claimActivation : 'inactive';
86
+ const observedAt = typeof raw.observedAt === 'number' && Number.isFinite(raw.observedAt)
87
+ ? raw.observedAt
88
+ : Date.now();
89
+ const turnExecutionId = typeof raw.turnExecutionId === 'string' && raw.turnExecutionId.trim().length > 0
90
+ ? raw.turnExecutionId
91
+ : undefined;
92
+ const claimContext = normalizeClaimContext(raw.claimContext);
93
+ const normalized = buildNormalizedAttentionSnapshot({
94
+ deliveryMode,
95
+ claimState,
96
+ claimActivation,
97
+ observedAt,
98
+ turnExecutionId,
99
+ claimContext,
100
+ });
101
+ if (normalized.claimContext?.scope === 'task-thread') {
102
+ return buildNormalizedAttentionSnapshot({
103
+ deliveryMode: 'default',
104
+ claimState: normalized.claimState,
105
+ claimActivation: 'dormant',
106
+ observedAt: normalized.observedAt,
107
+ turnExecutionId: normalized.turnExecutionId,
108
+ claimContext: normalized.claimContext,
109
+ });
110
+ }
111
+ return normalized;
112
+ }
113
+ function formatObservedAt(observedAt) {
114
+ return new Date(observedAt).toISOString();
115
+ }
116
+ export function buildAttentionSummaryLines(snapshot) {
117
+ if (!snapshot) {
118
+ return [];
119
+ }
120
+ return [
121
+ 'Host-observed attention snapshot for this channel (projection only; not server-authoritative truth).',
122
+ `Delivery mode: ${snapshot.deliveryMode}`,
123
+ `Claim state: ${snapshot.claimState}`,
124
+ `Claim activation: ${snapshot.claimActivation}`,
125
+ `Wake mode: ${snapshot.wakeMode}`,
126
+ ...(snapshot.claimContext
127
+ ? [
128
+ `Claim scope: ${snapshot.claimContext.scope}`,
129
+ `Claim task id: ${snapshot.claimContext.taskId}`,
130
+ ]
131
+ : []),
132
+ `Observed at: ${formatObservedAt(snapshot.observedAt)}`,
133
+ ...(snapshot.turnExecutionId
134
+ ? [`Observed turn execution id: ${snapshot.turnExecutionId}`]
135
+ : []),
136
+ ];
137
+ }
@@ -0,0 +1,3 @@
1
+ import type { CollaborationCapabilityDeclaration, MissedCollaborationDiagnostic } from '../types.js';
2
+ export declare function buildCollaborationCapabilityDeclarationSummaryLines(declaration: CollaborationCapabilityDeclaration | undefined): string[];
3
+ export declare function buildMissedCollaborationDiagnosticSummaryLines(diagnostic: MissedCollaborationDiagnostic | undefined): string[];
@@ -0,0 +1,18 @@
1
+ export function buildCollaborationCapabilityDeclarationSummaryLines(declaration) {
2
+ if (!declaration) {
3
+ return [];
4
+ }
5
+ return [
6
+ 'Shared hosted collaboration capability declaration (projection only; hosted-path vocabulary only, not authorization, action availability, thread-binding authority, or standalone-runtime parity).',
7
+ `Capability declaration: ${JSON.stringify(declaration)}`,
8
+ ];
9
+ }
10
+ export function buildMissedCollaborationDiagnosticSummaryLines(diagnostic) {
11
+ if (!diagnostic) {
12
+ return [];
13
+ }
14
+ return [
15
+ 'Latest hosted missed-collaboration diagnostic (projection only; delivery, wake, or response explanation only).',
16
+ `Missed-collaboration diagnostic: ${JSON.stringify(diagnostic)}`,
17
+ ];
18
+ }
@@ -0,0 +1,2 @@
1
+ import type { CollaborationOutcomeSnapshot } from '../types.js';
2
+ export declare function buildCollaborationOutcomeSummaryLines(snapshot: CollaborationOutcomeSnapshot | undefined): string[];
@@ -0,0 +1,26 @@
1
+ function formatObservedAt(observedAt) {
2
+ return new Date(observedAt).toISOString();
3
+ }
4
+ export function buildCollaborationOutcomeSummaryLines(snapshot) {
5
+ if (!snapshot) {
6
+ return [];
7
+ }
8
+ return [
9
+ 'Host-observed collaboration outcome snapshot for this channel (projection only; not provider-authoritative truth).',
10
+ `Response state: ${snapshot.responseState}`,
11
+ `Delivery state: ${snapshot.deliveryState}`,
12
+ ...(snapshot.wakeState ? [`Wake state: ${snapshot.wakeState}`] : []),
13
+ ...(snapshot.blockedDetails
14
+ ? [
15
+ `Blocked question: ${snapshot.blockedDetails.question}`,
16
+ ...(snapshot.blockedDetails.reason
17
+ ? [`Blocked reason: ${snapshot.blockedDetails.reason}`]
18
+ : []),
19
+ ]
20
+ : []),
21
+ `Observed at: ${formatObservedAt(snapshot.observedAt)}`,
22
+ ...(snapshot.turnExecutionId
23
+ ? [`Observed turn execution id: ${snapshot.turnExecutionId}`]
24
+ : []),
25
+ ];
26
+ }
@@ -1,4 +1,4 @@
1
- import type { LocalhostGatewayBootstrapMetadata, ProviderCollaborationContext, SkillRuntimeBootstrapMetadata, TaskAssignmentThreadContext, TaskWorkspaceContext } from '../types.js';
1
+ import type { AttentionSnapshot, CollaborationCapabilityDeclaration, CollaborationOutcomeSnapshot, LocalhostGatewayBootstrapMetadata, MissedCollaborationDiagnostic, ProviderCollaborationContext, SkillRuntimeBootstrapMetadata, TaskThreadCollaborationContract, TaskAssignmentThreadContext, TaskWorkspaceContext } from '../types.js';
2
2
  export interface SkillRuntimeBootstrapPayload {
3
3
  skillDirectoryPath: string;
4
4
  nodeCliPath: string;
@@ -7,6 +7,11 @@ export interface SkillRuntimeBootstrapPayload {
7
7
  export interface ChannelContextPayload {
8
8
  schemaVersion: 1;
9
9
  channelId: string;
10
+ collaborationOutcome?: CollaborationOutcomeSnapshot;
11
+ attentionSnapshot?: AttentionSnapshot;
12
+ taskThreadCollaborationContract?: TaskThreadCollaborationContract;
13
+ collaborationCapabilities?: CollaborationCapabilityDeclaration;
14
+ missedCollaborationDiagnostic?: MissedCollaborationDiagnostic;
10
15
  skillRuntime?: SkillRuntimeBootstrapPayload;
11
16
  localhostGateway?: LocalhostGatewayBootstrapMetadata;
12
17
  taskAssignmentContext?: TaskAssignmentThreadContext;
@@ -29,11 +34,21 @@ export interface ChannelContextStore {
29
34
  prepare(input: {
30
35
  channelId: string;
31
36
  collaboration?: ProviderCollaborationContext;
37
+ collaborationOutcome?: CollaborationOutcomeSnapshot;
38
+ attentionSnapshot?: AttentionSnapshot;
39
+ taskThreadCollaborationContract?: TaskThreadCollaborationContract;
40
+ collaborationCapabilities?: CollaborationCapabilityDeclaration;
41
+ missedCollaborationDiagnostic?: MissedCollaborationDiagnostic;
32
42
  incomingMessageType?: string;
33
43
  incomingContent?: string;
34
44
  }): Promise<PreparedChannelContext>;
35
45
  prepare(channelId: string, options?: {
36
46
  collaboration?: ProviderCollaborationContext;
47
+ collaborationOutcome?: CollaborationOutcomeSnapshot;
48
+ attentionSnapshot?: AttentionSnapshot;
49
+ taskThreadCollaborationContract?: TaskThreadCollaborationContract;
50
+ collaborationCapabilities?: CollaborationCapabilityDeclaration;
51
+ missedCollaborationDiagnostic?: MissedCollaborationDiagnostic;
37
52
  incomingMessageType?: string;
38
53
  incomingContent?: string;
39
54
  }): Promise<PreparedChannelContext>;
@@ -97,10 +112,20 @@ export declare class FileChannelContextStore implements ChannelContextStore {
97
112
  prepare(inputOrChannelId: {
98
113
  channelId: string;
99
114
  collaboration?: ProviderCollaborationContext;
115
+ collaborationOutcome?: CollaborationOutcomeSnapshot;
116
+ attentionSnapshot?: AttentionSnapshot;
117
+ taskThreadCollaborationContract?: TaskThreadCollaborationContract;
118
+ collaborationCapabilities?: CollaborationCapabilityDeclaration;
119
+ missedCollaborationDiagnostic?: MissedCollaborationDiagnostic;
100
120
  incomingMessageType?: string;
101
121
  incomingContent?: string;
102
122
  } | string, options?: {
103
123
  collaboration?: ProviderCollaborationContext;
124
+ collaborationOutcome?: CollaborationOutcomeSnapshot;
125
+ attentionSnapshot?: AttentionSnapshot;
126
+ taskThreadCollaborationContract?: TaskThreadCollaborationContract;
127
+ collaborationCapabilities?: CollaborationCapabilityDeclaration;
128
+ missedCollaborationDiagnostic?: MissedCollaborationDiagnostic;
104
129
  incomingMessageType?: string;
105
130
  incomingContent?: string;
106
131
  }): Promise<PreparedChannelContext>;
@@ -57,10 +57,15 @@ function buildLocalhostGatewayAuthPayload(channelId, localhostGateway) {
57
57
  },
58
58
  };
59
59
  }
60
- function buildChannelContextPayload(channelId, skillRuntime, localhostGateway, options) {
60
+ function buildChannelContextPayload(channelId, collaborationOutcome, attentionSnapshot, taskThreadCollaborationContract, collaborationCapabilities, missedCollaborationDiagnostic, skillRuntime, localhostGateway, options) {
61
61
  return {
62
62
  schemaVersion: 1,
63
63
  channelId,
64
+ ...(collaborationOutcome ? { collaborationOutcome } : {}),
65
+ ...(attentionSnapshot ? { attentionSnapshot } : {}),
66
+ ...(taskThreadCollaborationContract ? { taskThreadCollaborationContract } : {}),
67
+ ...(collaborationCapabilities ? { collaborationCapabilities } : {}),
68
+ ...(missedCollaborationDiagnostic ? { missedCollaborationDiagnostic } : {}),
64
69
  ...(skillRuntime ? { skillRuntime: toSkillRuntimePayload(skillRuntime) } : {}),
65
70
  ...(localhostGateway ? { localhostGateway } : {}),
66
71
  ...(options?.taskAssignmentContext ? { taskAssignmentContext: options.taskAssignmentContext } : {}),
@@ -281,7 +286,7 @@ export class FileChannelContextStore {
281
286
  rootPath: resolveTaskWorkspaceDirectory(this.startupWorkspaceRootDir, input.channelId, taskAssignmentContext.currentTaskId),
282
287
  }
283
288
  : undefined;
284
- const payload = buildChannelContextPayload(input.channelId, skillRuntime, localhostGateway, taskAssignmentContext || taskWorkspace
289
+ const payload = buildChannelContextPayload(input.channelId, input.collaborationOutcome, input.attentionSnapshot, input.taskThreadCollaborationContract, input.collaborationCapabilities, input.missedCollaborationDiagnostic, skillRuntime, localhostGateway, taskAssignmentContext || taskWorkspace
285
290
  ? {
286
291
  ...(taskAssignmentContext ? { taskAssignmentContext } : {}),
287
292
  ...(taskWorkspace ? { taskWorkspace } : {}),
@@ -1,4 +1,8 @@
1
1
  import { AWAITING_USER_CONTROL_PREFIX } from '../providers/awaiting-user.js';
2
+ import { buildAttentionSummaryLines } from './attention.js';
3
+ import { buildCollaborationCapabilityDeclarationSummaryLines, buildMissedCollaborationDiagnosticSummaryLines, } from './collaboration-capabilities-diagnostics.js';
4
+ import { buildCollaborationOutcomeSummaryLines } from './collaboration-outcome.js';
5
+ import { buildTaskThreadCollaborationSummaryLines } from './task-thread-collaboration.js';
2
6
  function providerLabel(provider) {
3
7
  if (provider === 'copilot') {
4
8
  return 'GitHub Copilot';
@@ -89,6 +93,10 @@ function buildInboundMetadataLines(params) {
89
93
  return [
90
94
  `Incoming transport event kind: ${params.incomingEventKind?.trim() || 'message'}`,
91
95
  `Incoming semantic message type: ${params.incomingMessageType?.trim() || 'default'}`,
96
+ ...(() => {
97
+ const lines = buildTaskThreadCollaborationSummaryLines(params.promptContext?.taskThreadCollaborationContract);
98
+ return lines.length > 0 ? ['', ...lines] : [];
99
+ })(),
92
100
  ...buildTaskAssignmentPromptLines(params),
93
101
  ];
94
102
  }
@@ -101,10 +109,37 @@ function describeIdentity(identity) {
101
109
  return `${label}, kind=${identity.kind}`;
102
110
  }
103
111
  function buildTurnControlPromptLines(context) {
112
+ const taskThreadAttentionAvailable = context?.taskAssignmentContext?.active === true
113
+ && typeof context.taskAssignmentContext.currentTaskId === 'string'
114
+ && context.taskAssignmentContext.currentTaskId.trim().length > 0;
115
+ const attentionLines = context?.attentionSnapshot
116
+ ? [
117
+ 'Attention controls are projection-only host hints. They do not bypass server-side mention policy or widen message delivery.',
118
+ `To follow ordinary delivered human messages in this channel, add "attentionUpdate":"follow-channel".`,
119
+ `To stop follow-based human wake for this channel, add "attentionUpdate":"unfollow-channel".`,
120
+ `To mute ordinary delivered human wake while keeping explicit mentions and task assignments untouched, add "attentionUpdate":"mute-channel".`,
121
+ `To set a durable channel-level attention hint, add "attentionUpdate":"claim-channel".`,
122
+ ...(taskThreadAttentionAvailable
123
+ ? [
124
+ `To set a durable task-thread attention hint, add "attentionUpdate":"claim-task-thread" only when this turn has active injected task-thread context with a usable currentTaskId.`,
125
+ `To clear a local claim without changing deliveryMode, add "attentionUpdate":"unclaim-channel" or "attentionUpdate":"unclaim-task-thread".`,
126
+ ]
127
+ : [
128
+ `To clear a local channel claim without changing deliveryMode, add "attentionUpdate":"unclaim-channel".`,
129
+ ]),
130
+ ]
131
+ : [];
132
+ const attentionOnlyLine = context?.attentionSnapshot
133
+ ? [
134
+ `For an attention-only reply, keep a non-empty visible body and append: ${AWAITING_USER_CONTROL_PREFIX}{"kind":"attention-only","attentionUpdate":"follow-channel"}`,
135
+ ]
136
+ : [];
104
137
  if (!context) {
105
138
  return [
106
139
  `Only when you need host-local turn control, append exactly one final non-empty line starting with ${AWAITING_USER_CONTROL_PREFIX}.`,
107
140
  `For ordinary blocked-on-human turns, use: ${AWAITING_USER_CONTROL_PREFIX}{"kind":"awaiting-user","question":"<short question>","reason":"<short reason>"}`,
141
+ ...attentionLines,
142
+ ...attentionOnlyLine,
108
143
  'Do not emit multiple control lines, and do not use a control footer for ordinary answers.',
109
144
  ];
110
145
  }
@@ -123,6 +158,10 @@ function buildTurnControlPromptLines(context) {
123
158
  `When handing off to the named peer, use: ${AWAITING_USER_CONTROL_PREFIX}{"kind":"continue-to-peer"}`,
124
159
  `When finishing locally, use: ${AWAITING_USER_CONTROL_PREFIX}{"kind":"conclude-locally"}`,
125
160
  `If you are blocked on the human instead, use: ${AWAITING_USER_CONTROL_PREFIX}{"kind":"awaiting-user","question":"<short question>","reason":"<short reason>"}`,
161
+ ...(context.attentionSnapshot
162
+ ? ['You may add one attentionUpdate field to continue-to-peer, conclude-locally, or awaiting-user when you also need an attention change for this channel.']
163
+ : []),
164
+ ...attentionLines,
126
165
  'Do not emit multiple control lines, and do not use start-protocol while host-managed collaboration is already active.',
127
166
  ];
128
167
  }
@@ -151,6 +190,7 @@ function buildTurnControlPromptLines(context) {
151
190
  ...(participantSummary ? [`Visible kickoff participants: ${participantSummary}.`] : []),
152
191
  'This evaluation is read-only and silent: do not use auxiliary collaboration send commands, and do not draft a user-visible reply body for this turn.',
153
192
  `If the anchor message is genuinely requesting host-managed collaboration between the named agents, use: ${AWAITING_USER_CONTROL_PREFIX}{"kind":"start-protocol","rounds":<positive integer>}`,
193
+ 'Do not combine start-protocol with any attentionUpdate field.',
154
194
  'If collaboration should not start, omit the control footer entirely.',
155
195
  'Do not emit multiple control lines.',
156
196
  ];
@@ -158,6 +198,11 @@ function buildTurnControlPromptLines(context) {
158
198
  return [
159
199
  `Only when you need host-local turn control, append exactly one final non-empty line starting with ${AWAITING_USER_CONTROL_PREFIX}.`,
160
200
  `For ordinary blocked-on-human turns, use: ${AWAITING_USER_CONTROL_PREFIX}{"kind":"awaiting-user","question":"<short question>","reason":"<short reason>"}`,
201
+ ...(context?.attentionSnapshot
202
+ ? ['You may add one attentionUpdate field to awaiting-user when you also need an attention change for this channel.']
203
+ : []),
204
+ ...attentionLines,
205
+ ...attentionOnlyLine,
161
206
  'Do not emit multiple control lines, and do not use a control footer for ordinary answers.',
162
207
  ];
163
208
  }
@@ -184,6 +229,22 @@ export function buildPrompt(params) {
184
229
  incomingMessageType: params.incomingMessageType,
185
230
  promptContext: params.promptContext,
186
231
  }),
232
+ ...(() => {
233
+ const lines = buildCollaborationOutcomeSummaryLines(params.promptContext?.collaborationOutcome);
234
+ return lines.length > 0 ? ['', ...lines] : [];
235
+ })(),
236
+ ...(() => {
237
+ const lines = buildAttentionSummaryLines(params.promptContext?.attentionSnapshot);
238
+ return lines.length > 0 ? ['', ...lines] : [];
239
+ })(),
240
+ ...(() => {
241
+ const lines = buildCollaborationCapabilityDeclarationSummaryLines(params.promptContext?.collaborationCapabilities);
242
+ return lines.length > 0 ? ['', ...lines] : [];
243
+ })(),
244
+ ...(() => {
245
+ const lines = buildMissedCollaborationDiagnosticSummaryLines(params.promptContext?.missedCollaborationDiagnostic);
246
+ return lines.length > 0 ? ['', ...lines] : [];
247
+ })(),
187
248
  ...buildSkillRuntimePromptLines(params.promptContext),
188
249
  ...buildLocalhostGatewayPromptLines(params.promptContext),
189
250
  '',
@@ -0,0 +1,6 @@
1
+ import type { TaskThreadCollaborationContract } from '../types.js';
2
+ export declare function projectTaskThreadCollaborationContract(params: {
3
+ incomingMessageType?: string;
4
+ taskThreadContextActive: boolean;
5
+ }): TaskThreadCollaborationContract | undefined;
6
+ export declare function buildTaskThreadCollaborationSummaryLines(contract: TaskThreadCollaborationContract | undefined): string[];
@@ -0,0 +1,31 @@
1
+ export function projectTaskThreadCollaborationContract(params) {
2
+ if (!params.taskThreadContextActive) {
3
+ return undefined;
4
+ }
5
+ const turnRole = params.incomingMessageType?.trim() === 'task_assignment'
6
+ ? 'assignment'
7
+ : 'continuation';
8
+ return {
9
+ source: 'host-projected',
10
+ turnRole,
11
+ taskThreadContextActive: true,
12
+ mainResult: 'ordinary-final-reply-in-thread',
13
+ auxiliaryRoute: 'optional-auxiliary-send-or-escalation-only',
14
+ checkIn: 'status-update-intent-only',
15
+ blockedWork: 'in-thread-disclosure-only',
16
+ };
17
+ }
18
+ export function buildTaskThreadCollaborationSummaryLines(contract) {
19
+ if (!contract) {
20
+ return [];
21
+ }
22
+ return [
23
+ 'Shared task-thread collaboration contract for this turn (projection only; not runtime-authoritative truth).',
24
+ `Thread role: ${contract.turnRole}`,
25
+ `Active task-thread context: ${contract.taskThreadContextActive ? 'yes' : 'no'}`,
26
+ 'Main task result: return the ordinary final reply in this task thread.',
27
+ 'Auxiliary send or escalation: optional auxiliary route only, never the main completion path.',
28
+ 'Check-in: status-update intent only; no timer, heartbeat, send-route, scheduler, receipt, or recovery authority is implied here.',
29
+ 'Blocked work: disclose the blocker in-thread through the existing in-thread reply path, if one exists; no blocked-state authority or extra control kind is implied here.',
30
+ ];
31
+ }
@@ -11,7 +11,13 @@ function providerLabel(provider) {
11
11
  return 'Claude';
12
12
  }
13
13
  function toPromptContext(channelContext, input) {
14
- if (!channelContext && !input.collaboration) {
14
+ if (!channelContext
15
+ && !input.collaboration
16
+ && !input.collaborationOutcome
17
+ && !input.attentionSnapshot
18
+ && !input.collaborationCapabilities
19
+ && !input.missedCollaborationDiagnostic
20
+ && !input.taskThreadCollaborationContract) {
15
21
  return undefined;
16
22
  }
17
23
  return {
@@ -27,6 +33,22 @@ function toPromptContext(channelContext, input) {
27
33
  : {}),
28
34
  collaborationTurnExecutionId: input.collaboration?.turnExecutionId,
29
35
  collaborationTurnMode: input.collaboration?.turnMode,
36
+ collaborationOutcome: input.collaborationOutcome ?? channelContext?.payload.collaborationOutcome,
37
+ attentionSnapshot: input.attentionSnapshot ?? channelContext?.payload.attentionSnapshot,
38
+ ...(() => {
39
+ const collaborationCapabilities = input.collaborationCapabilities ?? channelContext?.payload.collaborationCapabilities;
40
+ return collaborationCapabilities ? { collaborationCapabilities } : {};
41
+ })(),
42
+ ...(() => {
43
+ const missedCollaborationDiagnostic = input.missedCollaborationDiagnostic ?? channelContext?.payload.missedCollaborationDiagnostic;
44
+ return missedCollaborationDiagnostic ? { missedCollaborationDiagnostic } : {};
45
+ })(),
46
+ ...(input.taskThreadCollaborationContract || channelContext?.payload.taskThreadCollaborationContract
47
+ ? {
48
+ taskThreadCollaborationContract: input.taskThreadCollaborationContract
49
+ ?? channelContext?.payload.taskThreadCollaborationContract,
50
+ }
51
+ : {}),
30
52
  kickoff: input.collaboration?.kickoff,
31
53
  protocol: input.collaboration?.protocol,
32
54
  grounding: input.collaboration?.grounding,
@@ -65,6 +87,13 @@ export class ProviderTurnPreparer {
65
87
  && input.collaboration.turnMode !== 'silent-kickoff',
66
88
  }
67
89
  : undefined,
90
+ collaborationOutcome: input.collaborationOutcome,
91
+ attentionSnapshot: input.attentionSnapshot,
92
+ collaborationCapabilities: input.collaborationCapabilities,
93
+ missedCollaborationDiagnostic: input.missedCollaborationDiagnostic,
94
+ ...(input.taskThreadCollaborationContract
95
+ ? { taskThreadCollaborationContract: input.taskThreadCollaborationContract }
96
+ : {}),
68
97
  ...(input.incomingMessageType !== undefined
69
98
  ? { incomingMessageType: input.incomingMessageType }
70
99
  : {}),