@gajae-code/coding-agent 0.17.6 → 0.17.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/dist/types/cli/public-command-errors.d.ts +2 -0
- package/dist/types/commands/sdk.d.ts +7 -1
- package/dist/types/config/model-profile-ownership.d.ts +74 -0
- package/dist/types/config/model-registry.d.ts +2 -1
- package/dist/types/config/settings-schema.d.ts +32 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +4 -1
- package/dist/types/sdk/lifecycle/service.d.ts +2 -0
- package/dist/types/sdk/lifecycle-session.d.ts +5 -1
- package/dist/types/sdk/session-directory.d.ts +1 -0
- package/dist/types/sdk/session-list.d.ts +11 -0
- package/dist/types/sdk/startup-capability.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +6 -0
- package/dist/types/session/internal/managed-session-scope.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +6 -0
- package/dist/types/setup/model-onboarding-guidance.d.ts +18 -0
- package/dist/types/task/delegation-hint.d.ts +53 -0
- package/examples/README.md +3 -3
- package/examples/custom-tools/README.md +1 -1
- package/examples/hooks/README.md +1 -1
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +6 -0
- package/src/cli/config-cli.ts +2 -0
- package/src/cli/public-command-errors.ts +6 -0
- package/src/commands/sdk.ts +361 -117
- package/src/config/model-profile-ownership.ts +303 -0
- package/src/config/model-registry.ts +53 -14
- package/src/config/settings-schema.ts +81 -0
- package/src/internal-urls/docs-index.generated.ts +18 -17
- package/src/modes/controllers/extension-ui-controller.ts +6 -0
- package/src/modes/controllers/selector-controller.ts +3 -29
- package/src/sdk/broker/broker.ts +120 -23
- package/src/sdk/broker/lifecycle.ts +89 -13
- package/src/sdk/cli/session-cli.ts +45 -9
- package/src/sdk/host/control/dispatch.ts +6 -0
- package/src/sdk/lifecycle/service.ts +186 -3
- package/src/sdk/lifecycle-session.ts +35 -8
- package/src/sdk/protocol/operation-inventory.generated.json +33 -0
- package/src/sdk/protocol/operation-registry.ts +5 -1
- package/src/sdk/session-directory.ts +1 -1
- package/src/sdk/session-list.ts +47 -0
- package/src/sdk/session.ts +290 -114
- package/src/sdk/startup-capability.ts +2 -1
- package/src/session/agent-session.ts +102 -4
- package/src/session/internal/managed-session-scope.ts +81 -17
- package/src/session/session-manager.ts +70 -11
- package/src/setup/model-onboarding-guidance.ts +26 -0
- package/src/task/delegation-hint.ts +578 -0
|
@@ -11,6 +11,9 @@ import {
|
|
|
11
11
|
} from "../broker/session-scope";
|
|
12
12
|
import type { SessionBindingAuthority } from "../session-authority";
|
|
13
13
|
import {
|
|
14
|
+
SESSION_LIST_SAVED_SESSION_OMISSION_DETAIL_CODES,
|
|
15
|
+
type SessionListSavedSessionOmission,
|
|
16
|
+
type SessionListSavedSessionOmissionDetailCode,
|
|
14
17
|
SessionListTraversalError,
|
|
15
18
|
type SessionListTraversalPage,
|
|
16
19
|
sessionListPageFromResponse,
|
|
@@ -238,6 +241,7 @@ export interface SessionLifecycleListResult {
|
|
|
238
241
|
readonly sessions: readonly SessionLifecycleListEntry[];
|
|
239
242
|
readonly warnings: readonly string[];
|
|
240
243
|
readonly savedSession?: SessionLifecycleSavedSession;
|
|
244
|
+
readonly savedSessionOmission?: SessionListSavedSessionOmission;
|
|
241
245
|
}
|
|
242
246
|
|
|
243
247
|
export type SessionScopedListResult = SdkSearchResultV1;
|
|
@@ -652,11 +656,118 @@ function savedSessionTranscriptIdentity(value: unknown): SessionLifecycleSavedSe
|
|
|
652
656
|
function savedSessionFromResult(value: unknown): SessionLifecycleSavedSession | undefined {
|
|
653
657
|
if (!isRecord(value)) return undefined;
|
|
654
658
|
const identity = savedSessionTranscriptIdentity(value.identity);
|
|
655
|
-
if (
|
|
659
|
+
if (
|
|
660
|
+
typeof value.id !== "string" ||
|
|
661
|
+
!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value.id) ||
|
|
662
|
+
typeof value.path !== "string" ||
|
|
663
|
+
value.path.length === 0 ||
|
|
664
|
+
!identity
|
|
665
|
+
)
|
|
656
666
|
return undefined;
|
|
657
667
|
return { id: value.id, path: value.path, identity };
|
|
658
668
|
}
|
|
659
669
|
|
|
670
|
+
const savedSessionOmissionFields = new Set([
|
|
671
|
+
"sessionId",
|
|
672
|
+
"reason",
|
|
673
|
+
"detailCode",
|
|
674
|
+
"candidateCount",
|
|
675
|
+
"missingIdentityFields",
|
|
676
|
+
]);
|
|
677
|
+
const savedSessionOmissionDetailCodes = new Set<string>(SESSION_LIST_SAVED_SESSION_OMISSION_DETAIL_CODES);
|
|
678
|
+
const savedSessionScopeDetailCodes = new Set([
|
|
679
|
+
"cwd_missing",
|
|
680
|
+
"cwd_not_directory",
|
|
681
|
+
"identity_unavailable",
|
|
682
|
+
"network_unsupported",
|
|
683
|
+
"sessions_root_unavailable",
|
|
684
|
+
"binding_conflict",
|
|
685
|
+
"binding_invalid",
|
|
686
|
+
"migration_busy",
|
|
687
|
+
"atomic_unavailable",
|
|
688
|
+
"invalid_request",
|
|
689
|
+
"durability_failed",
|
|
690
|
+
"durability_not_provable",
|
|
691
|
+
"capacity_exceeded",
|
|
692
|
+
]);
|
|
693
|
+
const savedSessionScanDetailCodes = new Set(["scan_failed", "unsafe_root", "invalid_candidate"]);
|
|
694
|
+
const savedSessionCandidateDetailCodes = new Set([
|
|
695
|
+
"invalid_header",
|
|
696
|
+
"unreadable_candidate",
|
|
697
|
+
"source_changed",
|
|
698
|
+
"cwd_not_found",
|
|
699
|
+
"cwd_not_directory",
|
|
700
|
+
"cwd_not_utf8",
|
|
701
|
+
"cwd_network_unsupported",
|
|
702
|
+
"cwd_identity_unavailable",
|
|
703
|
+
"cwd_io_error",
|
|
704
|
+
]);
|
|
705
|
+
|
|
706
|
+
function isSafeSavedSessionOmissionDetailCode(value: unknown): value is SessionListSavedSessionOmissionDetailCode {
|
|
707
|
+
return typeof value === "string" && savedSessionOmissionDetailCodes.has(value);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
function savedSessionOmissionDetailMatchesReason(
|
|
711
|
+
reason: SessionListSavedSessionOmission["reason"],
|
|
712
|
+
detailCode: SessionListSavedSessionOmissionDetailCode,
|
|
713
|
+
): boolean {
|
|
714
|
+
if (reason === "scope_unavailable") return savedSessionScopeDetailCodes.has(detailCode);
|
|
715
|
+
if (reason === "candidate_scan_failed") return savedSessionScanDetailCodes.has(detailCode);
|
|
716
|
+
if (reason === "candidate_invalid") return savedSessionCandidateDetailCodes.has(detailCode);
|
|
717
|
+
return false;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
function savedSessionOmissionFromResult(value: unknown): SessionListSavedSessionOmission | undefined {
|
|
721
|
+
if (!isRecord(value)) return undefined;
|
|
722
|
+
if (Object.keys(value).some(field => !savedSessionOmissionFields.has(field))) return undefined;
|
|
723
|
+
if (
|
|
724
|
+
typeof value.sessionId !== "string" ||
|
|
725
|
+
!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value.sessionId) ||
|
|
726
|
+
(value.reason !== "scope_unavailable" &&
|
|
727
|
+
value.reason !== "candidate_scan_failed" &&
|
|
728
|
+
value.reason !== "candidate_not_found" &&
|
|
729
|
+
value.reason !== "candidate_ambiguous" &&
|
|
730
|
+
value.reason !== "candidate_invalid" &&
|
|
731
|
+
value.reason !== "identity_incomplete")
|
|
732
|
+
)
|
|
733
|
+
return undefined;
|
|
734
|
+
if (
|
|
735
|
+
value.detailCode !== undefined &&
|
|
736
|
+
(!isSafeSavedSessionOmissionDetailCode(value.detailCode) ||
|
|
737
|
+
!savedSessionOmissionDetailMatchesReason(value.reason, value.detailCode))
|
|
738
|
+
)
|
|
739
|
+
return undefined;
|
|
740
|
+
if (
|
|
741
|
+
value.candidateCount !== undefined &&
|
|
742
|
+
(value.reason !== "candidate_ambiguous" ||
|
|
743
|
+
typeof value.candidateCount !== "number" ||
|
|
744
|
+
!Number.isSafeInteger(value.candidateCount) ||
|
|
745
|
+
value.candidateCount < 2)
|
|
746
|
+
)
|
|
747
|
+
return undefined;
|
|
748
|
+
let missingIdentityFields: ("nlink" | "ctimeNs")[] | undefined;
|
|
749
|
+
if (value.missingIdentityFields !== undefined) {
|
|
750
|
+
if (
|
|
751
|
+
value.reason !== "identity_incomplete" ||
|
|
752
|
+
!Array.isArray(value.missingIdentityFields) ||
|
|
753
|
+
value.missingIdentityFields.length === 0 ||
|
|
754
|
+
value.missingIdentityFields.some(field => field !== "nlink" && field !== "ctimeNs") ||
|
|
755
|
+
new Set(value.missingIdentityFields).size !== value.missingIdentityFields.length
|
|
756
|
+
)
|
|
757
|
+
return undefined;
|
|
758
|
+
missingIdentityFields = value.missingIdentityFields as ("nlink" | "ctimeNs")[];
|
|
759
|
+
}
|
|
760
|
+
if (value.reason === "candidate_ambiguous" && value.candidateCount === undefined) return undefined;
|
|
761
|
+
if (value.reason === "identity_incomplete" && missingIdentityFields === undefined) return undefined;
|
|
762
|
+
return {
|
|
763
|
+
sessionId: value.sessionId,
|
|
764
|
+
reason: value.reason,
|
|
765
|
+
...(value.detailCode === undefined ? {} : { detailCode: value.detailCode }),
|
|
766
|
+
...(value.candidateCount === undefined ? {} : { candidateCount: value.candidateCount }),
|
|
767
|
+
...(missingIdentityFields === undefined ? {} : { missingIdentityFields }),
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
|
|
660
771
|
function listResult(value: unknown): SessionLifecycleListResult | undefined {
|
|
661
772
|
if (!isRecord(value) || !Array.isArray(value.sessions)) return undefined;
|
|
662
773
|
const indexSeq = value.indexSeq;
|
|
@@ -702,8 +813,19 @@ function listResult(value: unknown): SessionLifecycleListResult | undefined {
|
|
|
702
813
|
}
|
|
703
814
|
const savedSessionPresent = Object.hasOwn(value, "savedSession");
|
|
704
815
|
const savedSession = savedSessionPresent ? savedSessionFromResult(value.savedSession) : undefined;
|
|
705
|
-
|
|
706
|
-
|
|
816
|
+
const savedSessionOmissionPresent = Object.hasOwn(value, "savedSessionOmission");
|
|
817
|
+
const savedSessionOmission = savedSessionOmissionPresent
|
|
818
|
+
? savedSessionOmissionFromResult(value.savedSessionOmission)
|
|
819
|
+
: undefined;
|
|
820
|
+
if (savedSessionPresent && (!savedSession || savedSessionOmissionPresent)) return undefined;
|
|
821
|
+
if (savedSessionOmissionPresent && !savedSessionOmission) return undefined;
|
|
822
|
+
return {
|
|
823
|
+
indexSeq,
|
|
824
|
+
sessions,
|
|
825
|
+
warnings,
|
|
826
|
+
...(savedSession ? { savedSession } : {}),
|
|
827
|
+
...(savedSessionOmission ? { savedSessionOmission } : {}),
|
|
828
|
+
};
|
|
707
829
|
}
|
|
708
830
|
|
|
709
831
|
function scopedPage(value: unknown): { scope: SdkSearchResultV1["scope"]; observedAt: string } | undefined {
|
|
@@ -993,6 +1115,13 @@ export class SessionLifecycleService {
|
|
|
993
1115
|
"malformed_response",
|
|
994
1116
|
"lifecycle broker returned a malformed scoped list result",
|
|
995
1117
|
);
|
|
1118
|
+
if (result.savedSession !== undefined || result.savedSessionOmission !== undefined)
|
|
1119
|
+
return failure(
|
|
1120
|
+
"session.list",
|
|
1121
|
+
"uncertain",
|
|
1122
|
+
"malformed_response",
|
|
1123
|
+
"lifecycle broker attached an exact saved session to a scoped list result",
|
|
1124
|
+
);
|
|
996
1125
|
if (JSON.stringify(pageScope.scope) !== JSON.stringify(locallyResolvedScope))
|
|
997
1126
|
return failure(
|
|
998
1127
|
"session.list",
|
|
@@ -1178,6 +1307,35 @@ export class SessionLifecycleService {
|
|
|
1178
1307
|
const scopeRequest = target.scope === undefined ? undefined : scopeRequestV1(target.scope);
|
|
1179
1308
|
if (target.scope !== undefined && !scopeRequest)
|
|
1180
1309
|
return failure("session.list", "terminal", "invalid_request", "scope must be a valid ScopeRequestV1");
|
|
1310
|
+
if (target.cwd !== undefined && (typeof target.cwd !== "string" || target.cwd.length === 0))
|
|
1311
|
+
return failure("session.list", "terminal", "invalid_request", "cwd must be a non-empty string");
|
|
1312
|
+
if (target.cursor !== undefined && (typeof target.cursor !== "string" || target.cursor.length === 0))
|
|
1313
|
+
return failure("session.list", "terminal", "invalid_request", "cursor must be a non-empty opaque string");
|
|
1314
|
+
if (
|
|
1315
|
+
target.resolveSessionId !== undefined &&
|
|
1316
|
+
(typeof target.resolveSessionId !== "string" ||
|
|
1317
|
+
!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(target.resolveSessionId))
|
|
1318
|
+
)
|
|
1319
|
+
return failure(
|
|
1320
|
+
"session.list",
|
|
1321
|
+
"terminal",
|
|
1322
|
+
"invalid_request",
|
|
1323
|
+
"resolveSessionId must be a canonical safe identifier",
|
|
1324
|
+
);
|
|
1325
|
+
if (scopeRequest !== undefined && (target.cwd !== undefined || target.resolveSessionId !== undefined))
|
|
1326
|
+
return failure(
|
|
1327
|
+
"session.list",
|
|
1328
|
+
"terminal",
|
|
1329
|
+
"invalid_request",
|
|
1330
|
+
"cwd and resolveSessionId cannot be combined with a scoped session.list target",
|
|
1331
|
+
);
|
|
1332
|
+
if (target.cursor !== undefined && target.resolveSessionId !== undefined)
|
|
1333
|
+
return failure(
|
|
1334
|
+
"session.list",
|
|
1335
|
+
"terminal",
|
|
1336
|
+
"invalid_request",
|
|
1337
|
+
"cursor cannot be combined with resolveSessionId",
|
|
1338
|
+
);
|
|
1181
1339
|
let locallyResolvedScope: SdkSearchResultV1["scope"] | undefined;
|
|
1182
1340
|
if (scopeRequest !== undefined) {
|
|
1183
1341
|
try {
|
|
@@ -1253,6 +1411,30 @@ export class SessionLifecycleService {
|
|
|
1253
1411
|
"lifecycle broker returned a malformed list result",
|
|
1254
1412
|
);
|
|
1255
1413
|
const { result } = firstPage.page;
|
|
1414
|
+
const requestedSessionId = typeof target.resolveSessionId === "string" ? target.resolveSessionId : undefined;
|
|
1415
|
+
const exactSavedSessionTarget = requestedSessionId !== undefined && typeof target.cwd === "string";
|
|
1416
|
+
if (requestedSessionId !== undefined && pages.length > 1)
|
|
1417
|
+
return failure(
|
|
1418
|
+
"session.list",
|
|
1419
|
+
"uncertain",
|
|
1420
|
+
"malformed_response",
|
|
1421
|
+
"lifecycle broker paginated an exact session.list result",
|
|
1422
|
+
);
|
|
1423
|
+
for (const page of pages) {
|
|
1424
|
+
const { savedSession, savedSessionOmission } = page.page.result;
|
|
1425
|
+
if (savedSession === undefined && savedSessionOmission === undefined) continue;
|
|
1426
|
+
if (
|
|
1427
|
+
!exactSavedSessionTarget ||
|
|
1428
|
+
(savedSession !== undefined && savedSession.id !== requestedSessionId) ||
|
|
1429
|
+
(savedSessionOmission !== undefined && savedSessionOmission.sessionId !== requestedSessionId)
|
|
1430
|
+
)
|
|
1431
|
+
return failure(
|
|
1432
|
+
"session.list",
|
|
1433
|
+
"uncertain",
|
|
1434
|
+
"malformed_response",
|
|
1435
|
+
"lifecycle broker returned a saved session that does not match the requested target",
|
|
1436
|
+
);
|
|
1437
|
+
}
|
|
1256
1438
|
if (request.target?.scope !== undefined) {
|
|
1257
1439
|
const scope = firstPage.page.scope;
|
|
1258
1440
|
const observedAt = firstPage.page.observedAt;
|
|
@@ -1323,6 +1505,7 @@ export class SessionLifecycleService {
|
|
|
1323
1505
|
sessions: pages.flatMap(page => page.page.result.sessions),
|
|
1324
1506
|
warnings: result.warnings,
|
|
1325
1507
|
...(result.savedSession === undefined ? {} : { savedSession: result.savedSession }),
|
|
1508
|
+
...(result.savedSessionOmission === undefined ? {} : { savedSessionOmission: result.savedSessionOmission }),
|
|
1326
1509
|
},
|
|
1327
1510
|
};
|
|
1328
1511
|
}
|
|
@@ -59,13 +59,37 @@ export type CreateLifecycleAgentSessionOptions = Omit<CreateAgentSessionOptions,
|
|
|
59
59
|
lifecycleRequestId?: string;
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
+
export interface SdkLifecycleStartupOwner {
|
|
63
|
+
readonly capability: SdkStartupCapability;
|
|
64
|
+
readonly rollback: SdkStartupRollbackTracker;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function disposePartialSession(session: AgentSession): Promise<void> {
|
|
68
|
+
try {
|
|
69
|
+
await session.dispose();
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (!isSessionDisposalIncompleteError(error)) throw error;
|
|
72
|
+
await session.awaitDisposeCompletion();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
62
76
|
/** Internal lifecycle-only session construction with an owner-bound SDK startup result. */
|
|
63
77
|
export async function createLifecycleAgentSession(
|
|
64
78
|
options: CreateLifecycleAgentSessionOptions = {},
|
|
79
|
+
owner?: SdkLifecycleStartupOwner,
|
|
65
80
|
): Promise<CreateLifecycleAgentSessionResult> {
|
|
66
|
-
const rollback = new SdkStartupRollbackTracker();
|
|
67
|
-
const capability =
|
|
81
|
+
const rollback = owner?.rollback ?? new SdkStartupRollbackTracker();
|
|
82
|
+
const capability =
|
|
83
|
+
owner?.capability ??
|
|
84
|
+
new SdkStartupCapability(rollback, options.readiness ?? "immediate", options.lifecycleRequestId);
|
|
68
85
|
try {
|
|
86
|
+
if (capability.cancelled) {
|
|
87
|
+
const failure =
|
|
88
|
+
capability.result?.status === "failed"
|
|
89
|
+
? capability.result.failure
|
|
90
|
+
: capability.normalizeFailure("startup", "pending");
|
|
91
|
+
return { capability, rollback, failure };
|
|
92
|
+
}
|
|
69
93
|
const {
|
|
70
94
|
modelId,
|
|
71
95
|
mcpStartupTimeoutMs,
|
|
@@ -91,6 +115,14 @@ export async function createLifecycleAgentSession(
|
|
|
91
115
|
[lifecycleMcpStartupTimeoutOption]?: number;
|
|
92
116
|
};
|
|
93
117
|
const result = await createAgentSession(internalOptions);
|
|
118
|
+
if (capability.cancelled) {
|
|
119
|
+
await disposePartialSession(result.session);
|
|
120
|
+
const failure =
|
|
121
|
+
capability.result?.status === "failed"
|
|
122
|
+
? capability.result.failure
|
|
123
|
+
: capability.normalizeFailure("startup", "pending");
|
|
124
|
+
return { capability, rollback, failure };
|
|
125
|
+
}
|
|
94
126
|
// Explicit model pin (#4707) is a guarantee, not a preference. The
|
|
95
127
|
// coordinator validated the selector against its own registry; this child
|
|
96
128
|
// owns the registry that actually serves requests, and the two can drift
|
|
@@ -106,12 +138,7 @@ export async function createLifecycleAgentSession(
|
|
|
106
138
|
const expected = parseModelString(modelId);
|
|
107
139
|
const expectedSelector = expected ? `${expected.provider}/${expected.id}` : modelId;
|
|
108
140
|
if (!activeSelector || activeSelector.toLowerCase() !== expectedSelector.toLowerCase()) {
|
|
109
|
-
|
|
110
|
-
await result.session.dispose();
|
|
111
|
-
} catch (error) {
|
|
112
|
-
if (!isSessionDisposalIncompleteError(error)) throw error;
|
|
113
|
-
await result.session.awaitDisposeCompletion();
|
|
114
|
-
}
|
|
141
|
+
await disposePartialSession(result.session);
|
|
115
142
|
throw new Error(
|
|
116
143
|
`Model "${modelId}" not found. Use --list-models to see available models.${
|
|
117
144
|
activeSelector ? ` Session resolved ${activeSelector} instead.` : ""
|
|
@@ -2910,6 +2910,28 @@
|
|
|
2910
2910
|
"testIds": "not_applicable"
|
|
2911
2911
|
}
|
|
2912
2912
|
},
|
|
2913
|
+
{
|
|
2914
|
+
"sourceId": "agent_session:setCoordinatorRuntimeStateFileForTests",
|
|
2915
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
2916
|
+
"sourceKind": "agent_session",
|
|
2917
|
+
"decision": "exclude",
|
|
2918
|
+
"rationale": "test-only coordinator runtime state file setup seam, not a user-facing SDK control seam",
|
|
2919
|
+
"exclusionMetadata": {
|
|
2920
|
+
"adapterMappings": "not_applicable",
|
|
2921
|
+
"testIds": "not_applicable"
|
|
2922
|
+
}
|
|
2923
|
+
},
|
|
2924
|
+
{
|
|
2925
|
+
"sourceId": "agent_session:getCoordinatorRuntimeStateFileForTests",
|
|
2926
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
2927
|
+
"sourceKind": "agent_session",
|
|
2928
|
+
"decision": "exclude",
|
|
2929
|
+
"rationale": "read-only test-only coordinator runtime state file accessor, not a user-facing SDK control seam",
|
|
2930
|
+
"exclusionMetadata": {
|
|
2931
|
+
"adapterMappings": "not_applicable",
|
|
2932
|
+
"testIds": "not_applicable"
|
|
2933
|
+
}
|
|
2934
|
+
},
|
|
2913
2935
|
{
|
|
2914
2936
|
"sourceId": "agent_session:trackPostPromptTaskForTests",
|
|
2915
2937
|
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
@@ -2987,6 +3009,17 @@
|
|
|
2987
3009
|
"testIds": "not_applicable"
|
|
2988
3010
|
}
|
|
2989
3011
|
},
|
|
3012
|
+
{
|
|
3013
|
+
"sourceId": "agent_session:trackCoordinatorRuntimeStatePersistenceFailuresForTests",
|
|
3014
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
3015
|
+
"sourceKind": "agent_session",
|
|
3016
|
+
"decision": "exclude",
|
|
3017
|
+
"rationale": "test-only coordinator persistence failure observation seam, not a user-facing SDK control seam",
|
|
3018
|
+
"exclusionMetadata": {
|
|
3019
|
+
"adapterMappings": "not_applicable",
|
|
3020
|
+
"testIds": "not_applicable"
|
|
3021
|
+
}
|
|
3022
|
+
},
|
|
2990
3023
|
{
|
|
2991
3024
|
"sourceId": "agent_session:awaitCoordinatorRuntimeStatePersistenceForTests",
|
|
2992
3025
|
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
@@ -263,11 +263,15 @@ function controlDisposition(id: string): Record<Adapter, AdapterDisposition> {
|
|
|
263
263
|
|
|
264
264
|
function controlErrors(id: string): string[] {
|
|
265
265
|
const errors: Record<string, string[]> = {
|
|
266
|
-
C01
|
|
266
|
+
// C01/C05 submit a prompt and therefore run the prompt preflight, which
|
|
267
|
+
// refuses a session with no selected model before admission. The code is
|
|
268
|
+
// appended: ACP machine-only mapping reads errorCodes[0].
|
|
269
|
+
C01: ["client_ref_conflict", "reconciliation_capacity", "reconciliation_persist_failed", "model_not_selected"],
|
|
267
270
|
C02: ["client_ref_conflict", "reconciliation_capacity", "reconciliation_persist_failed"],
|
|
268
271
|
C09: ["client_ref_conflict", "reconciliation_capacity", "reconciliation_persist_failed"],
|
|
269
272
|
C06: ["action_claimed"],
|
|
270
273
|
C07: ["action_claimed", "terminal_uncertain"],
|
|
274
|
+
C05: ["invalid_request", "busy", "model_not_selected"],
|
|
271
275
|
C08: ["action_claimed", "terminal_uncertain"],
|
|
272
276
|
C13: ["invalid_request", "busy", "default_model_selection_recovery"],
|
|
273
277
|
C36: ["revision_conflict", "secret_input_forbidden"],
|
|
@@ -51,7 +51,7 @@ export type ListManagedSessionCandidatesResult =
|
|
|
51
51
|
scope: ManagedSessionScope;
|
|
52
52
|
owned: readonly LogicalSessionCandidate[];
|
|
53
53
|
foreignCount: number;
|
|
54
|
-
invalid: readonly { code: string }[];
|
|
54
|
+
invalid: readonly { code: string; sessionId?: string }[];
|
|
55
55
|
}
|
|
56
56
|
| { kind: "error"; code: "scan_failed" | "unsafe_root" | "invalid_candidate"; message: string };
|
|
57
57
|
|
package/src/sdk/session-list.ts
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
export type SessionListPageRecord = Record<string, unknown>;
|
|
2
2
|
|
|
3
|
+
export type SessionListSavedSessionOmissionReason =
|
|
4
|
+
| "scope_unavailable"
|
|
5
|
+
| "candidate_scan_failed"
|
|
6
|
+
| "candidate_not_found"
|
|
7
|
+
| "candidate_ambiguous"
|
|
8
|
+
| "candidate_invalid"
|
|
9
|
+
| "identity_incomplete";
|
|
10
|
+
|
|
11
|
+
export const SESSION_LIST_SAVED_SESSION_OMISSION_DETAIL_CODES = [
|
|
12
|
+
"cwd_missing",
|
|
13
|
+
"cwd_not_directory",
|
|
14
|
+
"identity_unavailable",
|
|
15
|
+
"network_unsupported",
|
|
16
|
+
"sessions_root_unavailable",
|
|
17
|
+
"binding_conflict",
|
|
18
|
+
"binding_invalid",
|
|
19
|
+
"migration_busy",
|
|
20
|
+
"atomic_unavailable",
|
|
21
|
+
"invalid_request",
|
|
22
|
+
"durability_failed",
|
|
23
|
+
"durability_not_provable",
|
|
24
|
+
"capacity_exceeded",
|
|
25
|
+
"scan_failed",
|
|
26
|
+
"unsafe_root",
|
|
27
|
+
"invalid_candidate",
|
|
28
|
+
"invalid_header",
|
|
29
|
+
"unreadable_candidate",
|
|
30
|
+
"source_changed",
|
|
31
|
+
"cwd_not_found",
|
|
32
|
+
"cwd_not_utf8",
|
|
33
|
+
"cwd_network_unsupported",
|
|
34
|
+
"cwd_identity_unavailable",
|
|
35
|
+
"cwd_io_error",
|
|
36
|
+
] as const;
|
|
37
|
+
|
|
38
|
+
export type SessionListSavedSessionOmissionDetailCode =
|
|
39
|
+
(typeof SESSION_LIST_SAVED_SESSION_OMISSION_DETAIL_CODES)[number];
|
|
40
|
+
|
|
41
|
+
/** Path-free diagnostic for a requested managed transcript that could not be selected safely. */
|
|
42
|
+
export interface SessionListSavedSessionOmission {
|
|
43
|
+
readonly sessionId: string;
|
|
44
|
+
readonly reason: SessionListSavedSessionOmissionReason;
|
|
45
|
+
readonly detailCode?: SessionListSavedSessionOmissionDetailCode;
|
|
46
|
+
readonly candidateCount?: number;
|
|
47
|
+
readonly missingIdentityFields?: readonly ("nlink" | "ctimeNs")[];
|
|
48
|
+
}
|
|
49
|
+
|
|
3
50
|
export type SessionListTraversalErrorKind = "malformed_page" | "repeated_cursor" | "page_budget_exceeded";
|
|
4
51
|
|
|
5
52
|
export class SessionListTraversalError extends Error {
|