@agent-link/agent 0.1.343 → 0.1.345
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/dist/backends/types.d.ts +6 -0
- package/dist/backends/types.js +2 -0
- package/dist/backends/types.js.map +1 -1
- package/dist/connection.d.ts +1 -0
- package/dist/connection.js +24 -5
- package/dist/connection.js.map +1 -1
- package/dist/copilot-command-bridge.d.ts +4 -0
- package/dist/copilot-command-bridge.js +57 -0
- package/dist/copilot-command-bridge.js.map +1 -1
- package/dist/copilot-user-requests.d.ts +1 -1
- package/dist/copilot-user-requests.js +7 -1
- package/dist/copilot-user-requests.js.map +1 -1
- package/dist/copilot.d.ts +18 -0
- package/dist/copilot.js +188 -2
- package/dist/copilot.js.map +1 -1
- package/dist/history-message-transform.d.ts +3 -0
- package/dist/history-message-transform.js.map +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +1 -1
- package/package.json +1 -1
package/dist/copilot.js
CHANGED
|
@@ -360,6 +360,7 @@ function sessionRef(sessionId) {
|
|
|
360
360
|
};
|
|
361
361
|
}
|
|
362
362
|
function startCopilotTurn(state) {
|
|
363
|
+
state.steeringRequests = undefined;
|
|
363
364
|
state.toolStartKeys = new Set();
|
|
364
365
|
state.compactionRecord = undefined;
|
|
365
366
|
state.reviewDocuments = new CopilotReviewDocuments();
|
|
@@ -418,6 +419,7 @@ function startCopilotTurn(state) {
|
|
|
418
419
|
}
|
|
419
420
|
}
|
|
420
421
|
function finishCopilotTurn(state) {
|
|
422
|
+
settleCopilotSteering(state, 'failed');
|
|
421
423
|
settleCopilotUserRequest(state, 'failed');
|
|
422
424
|
state.turnActive = false;
|
|
423
425
|
state.lastActivityAt = Date.now();
|
|
@@ -882,6 +884,171 @@ export function getCopilotConversation(conversationId) {
|
|
|
882
884
|
conversationAliases.delete(conversationId);
|
|
883
885
|
return undefined;
|
|
884
886
|
}
|
|
887
|
+
function copilotSteeringReceipt(state, request) {
|
|
888
|
+
const sessionId = state.commandSessionId || state.copilotSessionId || state.lastCopilotSessionId;
|
|
889
|
+
return { type: 'backend_event', providerId: request.providerId, conversationId: state.conversationId,
|
|
890
|
+
workDir: state.workDir, claudeSessionId: sessionId, turnId: request.backendTurnId, stateVersion: state.stateVersion,
|
|
891
|
+
event: { type: 'user_message', session: sessionRef(sessionId || 'pending'), message: { ...request } } };
|
|
892
|
+
}
|
|
893
|
+
export function getCopilotSteeringReceipts() {
|
|
894
|
+
return [...conversations.values()].flatMap(state => [...(state.steeringRequests?.values() || [])]
|
|
895
|
+
.filter(request => request.providerId === state.providerId && request.backendTurnId === state.turnId)
|
|
896
|
+
.map(request => copilotSteeringReceipt(state, request)));
|
|
897
|
+
}
|
|
898
|
+
function publishCopilotSteering(state, request) {
|
|
899
|
+
const sessionId = state.commandSessionId || state.copilotSessionId || state.lastCopilotSessionId;
|
|
900
|
+
try {
|
|
901
|
+
if (sessionId)
|
|
902
|
+
saveCopilotUserRequest(COPILOT_SESSION_DIR, sessionId, request);
|
|
903
|
+
}
|
|
904
|
+
finally {
|
|
905
|
+
sendFn(copilotSteeringReceipt(state, request));
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
function settleCopilotSteering(state, status) {
|
|
909
|
+
for (const request of state.steeringRequests?.values() || []) {
|
|
910
|
+
if (request.copilotRequestState !== 'pending')
|
|
911
|
+
continue;
|
|
912
|
+
request.copilotRequestState = status;
|
|
913
|
+
try {
|
|
914
|
+
publishCopilotSteering(state, request);
|
|
915
|
+
}
|
|
916
|
+
catch (error) {
|
|
917
|
+
console.warn('[Copilot:steering] Receipt persistence failed:', error);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
export async function handleCopilotSteering(prompt, scope, send) {
|
|
922
|
+
let state;
|
|
923
|
+
let request;
|
|
924
|
+
try {
|
|
925
|
+
if (typeof prompt !== 'string' || !prompt.trim() || prompt.trim().startsWith('/')
|
|
926
|
+
|| typeof scope.requestId !== 'string' || !/^[\w-]{1,100}$/.test(scope.requestId)) {
|
|
927
|
+
throw new Error('Steering requires text and a valid request ID');
|
|
928
|
+
}
|
|
929
|
+
state = getCopilotConversation(scope.conversationId);
|
|
930
|
+
const sessionId = state?.commandSessionId;
|
|
931
|
+
if (!state || scope.providerId !== 'copilot' || state.providerId !== scope.providerId
|
|
932
|
+
|| !sessionId || scope.claudeSessionId !== sessionId || typeof scope.workDir !== 'string'
|
|
933
|
+
|| resolve(scope.workDir) !== resolve(state.workDir) || !scope.turnId || scope.turnId !== state.turnId) {
|
|
934
|
+
throw new Error('Steering conversation, provider, directory, session or turn no longer matches');
|
|
935
|
+
}
|
|
936
|
+
state.steeringRequests ??= new Map();
|
|
937
|
+
const existing = state.steeringRequests.get(scope.requestId)
|
|
938
|
+
|| readCopilotUserRequests(COPILOT_SESSION_DIR, sessionId).find(row => row.copilotRequestId === scope.requestId);
|
|
939
|
+
if (existing) {
|
|
940
|
+
if (!existing.copilotSteering || existing.content !== prompt.trim() || existing.backendTurnId !== scope.turnId) {
|
|
941
|
+
publishCopilotSteering(state, existing);
|
|
942
|
+
send({ type: 'error', operation: 'copilot_request', providerId: scope.providerId,
|
|
943
|
+
conversationId: scope.conversationId, preserveActiveTurn: true,
|
|
944
|
+
message: 'Steering request ID was already used for a different request' });
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
publishCopilotSteering(state, existing);
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
if (!state.turnActive || state.isCompacting || state.copilotMode !== 'interactive'
|
|
951
|
+
|| state.planMode || state.planModePending || state.activeOperation !== 'chat'
|
|
952
|
+
|| getPendingCopilotDecisions(state.conversationId).length || !state.commandRuntime?.isRunning()) {
|
|
953
|
+
throw new Error('Steering is unavailable: the interactive task is no longer accepting requirements');
|
|
954
|
+
}
|
|
955
|
+
request = { role: 'user', content: prompt.trim(), copilotRequestId: scope.requestId,
|
|
956
|
+
backendTurnId: scope.turnId, providerId: scope.providerId, copilotSteering: true,
|
|
957
|
+
copilotRequestState: 'pending', timestamp: new Date().toISOString() };
|
|
958
|
+
state.steeringRequests.set(scope.requestId, request);
|
|
959
|
+
publishCopilotSteering(state, request);
|
|
960
|
+
const runtime = state.commandRuntime;
|
|
961
|
+
const nativeId = await runtime.steer(sessionId, request.content);
|
|
962
|
+
if (state.commandRuntime !== runtime || state.commandSessionId !== sessionId || state.turnId !== scope.turnId
|
|
963
|
+
|| state.providerId !== scope.providerId || resolve(state.workDir) !== resolve(scope.workDir)) {
|
|
964
|
+
console.warn('[Copilot:steering] Ignoring acknowledgment after ownership changed');
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
request.copilotUserMessageId = nativeId;
|
|
968
|
+
request.copilotRequestDispatched = true;
|
|
969
|
+
publishCopilotSteering(state, request);
|
|
970
|
+
}
|
|
971
|
+
catch (error) {
|
|
972
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
973
|
+
console.warn('[Copilot:steering]', message);
|
|
974
|
+
if (request && state && state.commandSessionId === scope.claudeSessionId && state.turnId === scope.turnId
|
|
975
|
+
&& state.providerId === scope.providerId && resolve(state.workDir) === resolve(scope.workDir)) {
|
|
976
|
+
if (request.copilotRequestState === 'pending')
|
|
977
|
+
request.copilotRequestState = 'failed';
|
|
978
|
+
request.copilotSteeringError = message;
|
|
979
|
+
try {
|
|
980
|
+
publishCopilotSteering(state, request);
|
|
981
|
+
}
|
|
982
|
+
catch (persistError) {
|
|
983
|
+
console.warn('[Copilot:steering] Failed receipt persistence:', persistError);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
else {
|
|
987
|
+
send({ type: 'backend_event', providerId: scope.providerId, conversationId: scope.conversationId,
|
|
988
|
+
workDir: scope.workDir, claudeSessionId: scope.claudeSessionId, turnId: scope.turnId,
|
|
989
|
+
event: { type: 'user_message', session: sessionRef(scope.claudeSessionId),
|
|
990
|
+
message: { role: 'user', content: typeof prompt === 'string' ? prompt : '', copilotSteering: true,
|
|
991
|
+
copilotRequestId: scope.requestId, copilotRequestState: 'failed', copilotSteeringError: message,
|
|
992
|
+
timestamp: new Date().toISOString() } } });
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
export async function handleCopilotBtwQuestion(question, scope, send) {
|
|
997
|
+
const reply = (message) => send({ type: 'btw_answer', ...scope, ...message });
|
|
998
|
+
try {
|
|
999
|
+
if (scope.providerId !== 'copilot') {
|
|
1000
|
+
reply({ delta: '', done: true, errorCode: 'btw_unsupported' });
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
if (!scope.requestId || typeof question !== 'string' || !question.trim()) {
|
|
1004
|
+
reply({ delta: '', done: true, errorCode: 'btw_invalid' });
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
const conversationId = scope.conversationId;
|
|
1008
|
+
const state = conversationId ? getCopilotConversation(conversationId) : undefined;
|
|
1009
|
+
const sessionId = state?.commandSessionId || state?.copilotSessionId || state?.lastCopilotSessionId;
|
|
1010
|
+
if (!conversationId || !state || !sessionId || !scope.claudeSessionId || scope.claudeSessionId !== sessionId
|
|
1011
|
+
|| state.providerId !== scope.providerId || resolve(state.workDir) !== resolve(scope.workDir)) {
|
|
1012
|
+
reply({ delta: '', done: true, errorCode: 'btw_no_session' });
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
markCopilotRuntimeActivity(state);
|
|
1016
|
+
if (state.runtimeInitPromise)
|
|
1017
|
+
await state.runtimeInitPromise;
|
|
1018
|
+
if (!state.commandRuntime?.isRunning()) {
|
|
1019
|
+
if (state.turnActive || state.isCompacting) {
|
|
1020
|
+
reply({ delta: '', done: true, errorCode: 'btw_unavailable' });
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
await ensureCopilotRuntime(state, sessionId);
|
|
1024
|
+
}
|
|
1025
|
+
const runtime = state.commandRuntime;
|
|
1026
|
+
const stillCurrent = () => getCopilotConversation(conversationId) === state
|
|
1027
|
+
&& state.providerId === scope.providerId
|
|
1028
|
+
&& resolve(state.workDir) === resolve(scope.workDir)
|
|
1029
|
+
&& state.commandRuntime === runtime && state.commandSessionId === sessionId;
|
|
1030
|
+
if (!runtime || !stillCurrent()) {
|
|
1031
|
+
reply({ delta: '', done: true, errorCode: 'btw_unavailable' });
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
const answer = await runtime.ephemeralQuery(sessionId, question.trim(), delta => {
|
|
1035
|
+
if (stillCurrent())
|
|
1036
|
+
reply({ delta, done: false });
|
|
1037
|
+
});
|
|
1038
|
+
if (stillCurrent()) {
|
|
1039
|
+
markCopilotRuntimeActivity(state);
|
|
1040
|
+
reply({ answer, delta: '', done: true });
|
|
1041
|
+
}
|
|
1042
|
+
else
|
|
1043
|
+
reply({ delta: '', done: true, errorCode: 'btw_unavailable' });
|
|
1044
|
+
}
|
|
1045
|
+
catch (error) {
|
|
1046
|
+
console.warn(`[Copilot:btw] ${error instanceof Error ? error.message : String(error)}`);
|
|
1047
|
+
const errorCode = error instanceof Error && 'code' in error && error.code === 'btw_busy'
|
|
1048
|
+
? 'btw_busy' : 'btw_failed';
|
|
1049
|
+
reply({ delta: '', done: true, errorCode, error: error instanceof Error ? error.message : String(error) });
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
885
1052
|
function rejectCopilotBusyRequest(conversationId, state) {
|
|
886
1053
|
// Reject this extra request, not the still-running turn shared by other pages.
|
|
887
1054
|
// Existing terminal-turn guards must not interpret it as the owner's failure.
|
|
@@ -3351,7 +3518,20 @@ function handleCopilotEvent(state, event) {
|
|
|
3351
3518
|
const id = data?.messageId || event.id;
|
|
3352
3519
|
if (typeof id !== 'string' || (!message && !attachments.length))
|
|
3353
3520
|
break;
|
|
3354
|
-
|
|
3521
|
+
const steering = [...(state.steeringRequests?.values() || [])].find(row => row.copilotUserMessageId === id)
|
|
3522
|
+
|| [...(state.steeringRequests?.values() || [])].find(row => !row.copilotUserMessageId
|
|
3523
|
+
&& row.content === content && ['steering', 'queued'].includes(data?.delivery));
|
|
3524
|
+
if (steering) {
|
|
3525
|
+
steering.copilotUserMessageId = id;
|
|
3526
|
+
steering.copilotRequestState = 'delivered';
|
|
3527
|
+
try {
|
|
3528
|
+
publishCopilotSteering(state, steering);
|
|
3529
|
+
}
|
|
3530
|
+
catch (error) {
|
|
3531
|
+
console.warn('[Copilot:steering] Could not save delivery:', error);
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
if (state.userRequest?.copilotRequestState === 'pending' && data?.delivery !== 'steering' && !steering) {
|
|
3355
3535
|
state.userRequest.copilotUserMessageId = id;
|
|
3356
3536
|
settleCopilotUserRequest(state, 'delivered');
|
|
3357
3537
|
}
|
|
@@ -3360,6 +3540,7 @@ function handleCopilotEvent(state, event) {
|
|
|
3360
3540
|
...(state.userRequest?.copilotUserMessageId === id ? {
|
|
3361
3541
|
copilotRequestId: state.userRequest.copilotRequestId, copilotRequestState: 'delivered',
|
|
3362
3542
|
} : {}),
|
|
3543
|
+
...(steering ? { ...steering } : data?.delivery === 'steering' ? { copilotSteering: true } : {}),
|
|
3363
3544
|
copilotUserMessageId: id, ...(attachments.length ? { attachments } : {}) } });
|
|
3364
3545
|
break;
|
|
3365
3546
|
}
|
|
@@ -3817,6 +3998,8 @@ function handleCopilotEvent(state, event) {
|
|
|
3817
3998
|
// ── Cancel ──────────────────────────────────────────────────────────────────
|
|
3818
3999
|
export function cancelCopilotExecution(conversationId) {
|
|
3819
4000
|
const state = getCopilotConversation(conversationId);
|
|
4001
|
+
if (state)
|
|
4002
|
+
settleCopilotSteering(state, 'cancelled');
|
|
3820
4003
|
if (state)
|
|
3821
4004
|
settleCopilotUserRequest(state, 'cancelled');
|
|
3822
4005
|
cancelPendingCopilotDecisions(state?.conversationId || conversationId);
|
|
@@ -5528,7 +5711,10 @@ function mergeCopilotRequestHistory(messages, sessionId) {
|
|
|
5528
5711
|
else
|
|
5529
5712
|
requests[index] = owner.userRequest;
|
|
5530
5713
|
}
|
|
5531
|
-
|
|
5714
|
+
const liveIds = [owner?.userRequest?.copilotRequestId,
|
|
5715
|
+
...[...(owner?.steeringRequests?.values() || [])].map(row => row.copilotRequestId)]
|
|
5716
|
+
.filter((id) => !!id);
|
|
5717
|
+
return mergeCopilotUserRequests(messages, requests, liveIds);
|
|
5532
5718
|
}
|
|
5533
5719
|
export function readCopilotSessionMode(sessionId) {
|
|
5534
5720
|
const sessionRoot = resolve(COPILOT_SESSION_DIR);
|