maka-agent 0.2.0-dev.14.20260902 → 0.2.0-dev.16.20260903
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/cli-core.js +0 -1
- package/dist/pi-transcript.js +48 -0
- package/dist/pi-tui-form-interaction.js +497 -0
- package/dist/pi-tui-pickers.js +256 -55
- package/dist/pi-tui-runner.js +215 -12
- package/dist/runtime-host-cli.js +17 -42
- package/dist/runtime-host-lifecycle-transaction.js +31 -13
- package/dist/runtime-host-managed-lifecycle-manager.js +1 -1
- package/dist/runtime-host-onboarding.js +3 -1
- package/dist/runtime-host-openrc-service.js +469 -0
- package/dist/runtime-host-run-command.js +6 -2
- package/dist/runtime-host-service-command.js +3 -3
- package/dist/runtime-host-service-management-command.js +43 -15
- package/dist/runtime-host-session-channel.js +3 -0
- package/dist/runtime-host-session-driver.js +21 -6
- package/dist/runtime-host-setup-command.js +30 -31
- package/dist/tui-context-refresh.js +103 -0
- package/dist/tui-copy-catalog.js +73 -0
- package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
- package/node_modules/@maka/core/dist/chat-model-choice.js +4 -0
- package/node_modules/@maka/core/dist/computer-use.js +27 -92
- package/node_modules/@maka/core/dist/interaction.js +577 -3
- package/node_modules/@maka/core/dist/llm-connections.js +4 -0
- package/node_modules/@maka/core/dist/local-memory.js +2 -2
- package/node_modules/@maka/core/dist/model-call-usage-projection.js +3 -0
- package/node_modules/@maka/core/dist/model-metadata.js +26 -7
- package/node_modules/@maka/core/dist/model-thinking.js +17 -0
- package/node_modules/@maka/core/dist/provider-registry.js +1 -0
- package/node_modules/@maka/core/dist/runtime-event.js +23 -1
- package/node_modules/@maka/core/dist/scheduled-task.js +4 -0
- package/node_modules/@maka/core/dist/session.js +95 -2
- package/node_modules/@maka/core/dist/text-sanitize.js +19 -0
- package/node_modules/@maka/core/dist/usage-ledger-merge.js +3 -0
- package/node_modules/@maka/core/dist/usage-record-schema.js +11 -5
- package/node_modules/@maka/core/dist/workhub-creation-intent.js +75 -9
- package/node_modules/@maka/core/dist/workhub-session-resolver.js +50 -0
- package/node_modules/@maka/core/package.json +2 -0
- package/node_modules/@maka/eval/dist/harbor-external-subject.js +2 -1
- package/node_modules/@maka/eval/dist/harness-executor.js +3 -2
- package/node_modules/@maka/eval/dist/install-preflight.js +39 -10
- package/node_modules/@maka/eval/dist/process-termination.js +83 -0
- package/node_modules/@maka/mcp/dist/sep-2243.js +3 -3
- package/node_modules/@maka/runtime/dist/agent-run.js +6 -3
- package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +332 -118
- package/node_modules/@maka/runtime/dist/ai-sdk-compaction-contract.js +6 -1
- package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +171 -341
- package/node_modules/@maka/runtime/dist/bots/telegram-bridge.js +3 -26
- package/node_modules/@maka/runtime/dist/bundled-skill-catalog.generated.js +1 -1
- package/node_modules/@maka/runtime/dist/computer-use-codec.js +1 -141
- package/node_modules/@maka/runtime/dist/computer-use-tools.js +23 -105
- package/node_modules/@maka/runtime/dist/context-budget-policy.js +20 -26
- package/node_modules/@maka/runtime/dist/context-budget.js +18 -20
- package/node_modules/@maka/runtime/dist/conversation-copy.js +23 -19
- package/node_modules/@maka/runtime/dist/cua-frame-state.js +1 -69
- package/node_modules/@maka/runtime/dist/github-copilot-oauth-enrollment.js +267 -0
- package/node_modules/@maka/runtime/dist/history-compact-checkpoint.js +6 -9
- package/node_modules/@maka/runtime/dist/history-compact-summarizer.js +67 -23
- package/node_modules/@maka/runtime/dist/history-compact-summary-validation.js +11 -18
- package/node_modules/@maka/runtime/dist/history-compaction.js +66 -73
- package/node_modules/@maka/runtime/dist/interaction-authority.js +69 -0
- package/node_modules/@maka/runtime/dist/mcp-tools.js +13 -1
- package/node_modules/@maka/runtime/dist/memory-extraction.js +11 -11
- package/node_modules/@maka/runtime/dist/model-adapter.js +24 -6
- package/node_modules/@maka/runtime/dist/model-factory.js +14 -5
- package/node_modules/@maka/runtime/dist/oauth-provider-contracts.js +28 -1
- package/node_modules/@maka/runtime/dist/openai-codex-history-compactor.js +7 -6
- package/node_modules/@maka/runtime/dist/opencode-session-header.js +29 -0
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +11 -1
- package/node_modules/@maka/runtime/dist/runtime-kernel.js +12 -11
- package/node_modules/@maka/runtime/dist/runtime-read-model.js +51 -13
- package/node_modules/@maka/runtime/dist/scheduled-task-tools.js +3 -0
- package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +26 -0
- package/node_modules/@maka/runtime/dist/session-manager.js +1 -0
- package/node_modules/@maka/runtime/dist/session-projection-helpers.js +16 -1
- package/node_modules/@maka/runtime/dist/stream-graph-projection.js +6 -0
- package/node_modules/@maka/runtime/dist/stream-graph-read-model.js +5 -0
- package/node_modules/@maka/runtime/dist/stream-usage-fallback-fetch.js +74 -0
- package/node_modules/@maka/runtime/dist/subscription-credentials.js +51 -4
- package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +47 -3
- package/node_modules/@maka/runtime/dist/system-prompt/main-session-prompt.js +20 -1
- package/node_modules/@maka/runtime/dist/test-connection.js +18 -11
- package/node_modules/@maka/runtime/dist/tool-runtime.js +183 -0
- package/node_modules/@maka/runtime/package.json +1 -0
- package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +14 -0
- package/node_modules/@maka/runtime-host/dist/client/client-capability-channel.js +84 -1
- package/node_modules/@maka/runtime-host/dist/client/host-profile.js +68 -51
- package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
- package/node_modules/@maka/runtime-host/dist/client/owner-connection-code.js +8 -5
- package/node_modules/@maka/runtime-host/dist/client/peer-client.js +342 -36
- package/node_modules/@maka/runtime-host/dist/client/reconnect-lifecycle.js +60 -4
- package/node_modules/@maka/runtime-host/dist/peer-mesh/index.js +3 -3
- package/node_modules/@maka/runtime-host/dist/peer-mesh/limits.js +1 -1
- package/node_modules/@maka/runtime-host/dist/peer-mesh/model.js +48 -60
- package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +847 -431
- package/node_modules/@maka/runtime-host/dist/peer-mesh/owner.js +33 -74
- package/node_modules/@maka/runtime-host/dist/peer-mesh/store.js +82 -69
- package/node_modules/@maka/runtime-host/dist/peer-reachability/index.js +21 -0
- package/node_modules/@maka/runtime-host/dist/peer-reachability/model.js +202 -0
- package/node_modules/@maka/runtime-host/dist/peer-reachability/owner.js +119 -0
- package/node_modules/@maka/runtime-host/dist/peer-reachability/publisher.js +245 -0
- package/node_modules/@maka/runtime-host/dist/protocol/agent-graph.js +4 -1
- package/node_modules/@maka/runtime-host/dist/protocol/client-capability.js +68 -0
- package/node_modules/@maka/runtime-host/dist/protocol/connection-effects.js +15 -8
- package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +7 -23
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +47 -1
- package/node_modules/@maka/runtime-host/dist/protocol/oauth.js +25 -1
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/peer-mesh.js +14 -31
- package/node_modules/@maka/runtime-host/dist/protocol/scheduled-task.js +19 -6
- package/node_modules/@maka/runtime-host/dist/protocol/session-collaboration.js +36 -1
- package/node_modules/@maka/runtime-host/dist/protocol/session-revision.js +11 -2
- package/node_modules/@maka/runtime-host/dist/protocol/usage-pricing.js +16 -2
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +51 -2
- package/node_modules/@maka/runtime-host/dist/server/access-authority.js +35 -2
- package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +1 -0
- package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +12 -10
- package/node_modules/@maka/runtime-host/dist/server/client-capability-invocation-broker.js +163 -30
- package/node_modules/@maka/runtime-host/dist/server/connection-effect-coordinator.js +38 -14
- package/node_modules/@maka/runtime-host/dist/server/daily-review-coordinator.js +45 -22
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +78 -20
- package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +4 -2
- package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +2 -1
- package/node_modules/@maka/runtime-host/dist/server/execution-service.js +49 -28
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +3 -6
- package/node_modules/@maka/runtime-host/dist/server/interaction-coordinator.js +130 -27
- package/node_modules/@maka/runtime-host/dist/server/interaction-projection.js +30 -1
- package/node_modules/@maka/runtime-host/dist/server/listener-set.js +2 -4
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +16 -3
- package/node_modules/@maka/runtime-host/dist/server/oauth-coordinator.js +66 -3
- package/node_modules/@maka/runtime-host/dist/server/oauth-execution-authority.js +20 -3
- package/node_modules/@maka/runtime-host/dist/server/peer-listener.js +8 -21
- package/node_modules/@maka/runtime-host/dist/server/peer-mesh-authority.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +6 -3
- package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +72 -12
- package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +48 -18
- package/node_modules/@maka/runtime-host/dist/server/session-turn-access-request-coordinator.js +43 -26
- package/node_modules/@maka/runtime-host/dist/server/usage-pricing-coordinator.js +15 -1
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +229 -4
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +146 -8
- package/node_modules/@maka/runtime-host/dist/transport/peer-native.js +32 -6
- package/node_modules/@maka/runtime-host/package.json +1 -0
- package/node_modules/@maka/storage/dist/agent-run-store.js +18 -5
- package/node_modules/@maka/storage/dist/codex-session-adapter.js +14 -0
- package/node_modules/@maka/storage/dist/execution-stores.js +5 -0
- package/node_modules/@maka/storage/dist/interaction-store.js +13 -1
- package/node_modules/@maka/storage/dist/runtime-policy/connection-catalog-document.js +9 -3
- package/node_modules/@maka/storage/dist/runtime-policy/coordinator.js +49 -19
- package/node_modules/@maka/storage/dist/runtime-policy/oauth-login-receipt-document.js +3 -1
- package/node_modules/@maka/storage/dist/runtime-policy/onboarding-transaction.js +13 -5
- package/node_modules/@maka/storage/dist/session-copy-cleanup.js +4 -2
- package/node_modules/@maka/storage/dist/session-store.js +39 -6
- package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +16 -9
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-schema.js +30 -3
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +117 -0
- package/node_modules/@maka/storage/dist/sqlite-usage-store.js +29 -6
- package/node_modules/@maka/storage/dist/usage-stores.js +3 -1
- package/package.json +1 -1
|
@@ -21,7 +21,7 @@ import { mkdir } from 'node:fs/promises';
|
|
|
21
21
|
import { join } from 'node:path';
|
|
22
22
|
import { isDeepStrictEqual } from 'node:util';
|
|
23
23
|
import { normalizeMessageContent } from '@maka/core/events';
|
|
24
|
-
import { WORKHUB_COORDINATION_SESSION_ID, WORKHUB_COORDINATION_SESSION_ROLE, WORKHUB_COORDINATION_REPLACEMENT_SCHEMA_VERSION, isWorkHubCoordinationSession, isWorkHubCoordinationSessionId, } from '@maka/core/session';
|
|
24
|
+
import { WORKHUB_COORDINATION_SESSION_ID, WORKHUB_COORDINATION_SESSION_ROLE, WORKHUB_COORDINATION_REPLACEMENT_SCHEMA_VERSION, WORKHUB_COORDINATION_STOP_SCHEMA_VERSION, isWorkHubCoordinationSession, isWorkHubCoordinationSessionId, } from '@maka/core/session';
|
|
25
25
|
import { WORKHUB_COORDINATION_SUMMARY_MAX_BYTES, WORKHUB_COORDINATION_TEXT_MAX_BYTES, } from '../protocol/index.js';
|
|
26
26
|
import { SessionOperationFailure } from './session-catalog-coordinator.js';
|
|
27
27
|
import { WorkHubActionEffectFailure, WorkHubActionGateFailure, WorkHubCoordinationActionGate, } from './workhub-coordination-action-gate.js';
|
|
@@ -58,9 +58,11 @@ export class HostWorkHubCoordinationCoordinator {
|
|
|
58
58
|
#resolveCreateTarget;
|
|
59
59
|
#requestDrain;
|
|
60
60
|
#actionGate;
|
|
61
|
+
#readDelegationRetirement;
|
|
61
62
|
constructor(options) {
|
|
62
63
|
this.#coordinationCwd = join(options.stateRoot, COORDINATION_CWD_DIRECTORY);
|
|
63
64
|
this.#stores = options.stores;
|
|
65
|
+
this.#readDelegationRetirement = options.sessionActions.readDelegationRetirement;
|
|
64
66
|
this.#admission = options.admission;
|
|
65
67
|
this.#continuity = options.continuity;
|
|
66
68
|
this.#executions = options.executions;
|
|
@@ -68,10 +70,21 @@ export class HostWorkHubCoordinationCoordinator {
|
|
|
68
70
|
this.#requestDrain = options.requestDrain;
|
|
69
71
|
this.#actionGate = new WorkHubCoordinationActionGate({
|
|
70
72
|
listSessions: () => this.#stores.listHeaders(),
|
|
73
|
+
// The global action owner is committed under the same Coordination
|
|
74
|
+
// admission that serializes every durable Coordination fact, so a
|
|
75
|
+
// concurrent action cannot slip between the claim and the fact it owns.
|
|
76
|
+
claimAction: (claim) => this.#admission.run(WORKHUB_COORDINATION_SESSION_ID, () => this.#stores.claimWorkHubAction(claim)),
|
|
77
|
+
// Read without the admission lease: it is a durable point lookup by
|
|
78
|
+
// primary key, and the claim it finds was committed under that lease.
|
|
79
|
+
readActionClaim: (actionId) => this.#stores.readWorkHubActionClaim(actionId),
|
|
80
|
+
probeTargetRemoval: async (sessionId) => (await this.#stores.probeSessionRemoval(sessionId)).kind,
|
|
71
81
|
readAssignment: (actionId) => this.#stores.readWorkHubAssignment(actionId),
|
|
82
|
+
listActiveAssignments: () => this.#listActiveAssignments(),
|
|
72
83
|
readReplacement: (delegationId) => this.#stores.readWorkHubReplacement(delegationId),
|
|
73
84
|
readReplacementAbort: (delegationId) => this.#stores.readWorkHubReplacementAbort(delegationId),
|
|
74
85
|
readSupersession: (delegationId) => this.#stores.readWorkHubSupersession(delegationId),
|
|
86
|
+
readStopRequest: (delegationId) => this.#stores.readWorkHubStopRequest(delegationId),
|
|
87
|
+
readStopResolution: (delegationId) => this.#stores.readWorkHubStopResolution(delegationId),
|
|
75
88
|
answer: async (input, context) => {
|
|
76
89
|
const outcome = await this.#answer({ turnId: input.turnId, text: input.text }, context);
|
|
77
90
|
if (!outcome.ok) {
|
|
@@ -91,13 +104,15 @@ export class HostWorkHubCoordinationCoordinator {
|
|
|
91
104
|
assign: options.sessionActions.assign,
|
|
92
105
|
prepareReplacement: (input) => this.#prepareReplacement(input),
|
|
93
106
|
abortReplacement: (input) => this.#abortReplacement(input),
|
|
107
|
+
prepareStop: (input) => this.#prepareStop(input),
|
|
108
|
+
resolveStop: (input) => this.#resolveStop(input),
|
|
94
109
|
readDelegationRetirement: options.sessionActions.readDelegationRetirement,
|
|
95
110
|
retireDelegation: options.sessionActions.retireDelegation,
|
|
96
111
|
});
|
|
97
112
|
}
|
|
98
113
|
#prepareReplacement(input) {
|
|
99
|
-
const suffix =
|
|
100
|
-
return this.#
|
|
114
|
+
const suffix = workHubDestructiveClaimIdentitySuffix(input.replacesDelegationId);
|
|
115
|
+
return this.#commitCoordinationFact({
|
|
101
116
|
read: () => this.#stores.readWorkHubReplacement(input.replacesDelegationId),
|
|
102
117
|
build: (existing) => ({
|
|
103
118
|
type: 'workhub_coordination',
|
|
@@ -121,6 +136,13 @@ export class HostWorkHubCoordinationCoordinator {
|
|
|
121
136
|
}),
|
|
122
137
|
conflictMessage: 'WorkHub action identity belongs to a different replacement',
|
|
123
138
|
beforeAppend: async () => {
|
|
139
|
+
const stopRequest = await this.#stores.readWorkHubStopRequest(input.replacesDelegationId);
|
|
140
|
+
if (stopRequest) {
|
|
141
|
+
const resolution = await this.#stores.readWorkHubStopResolution(input.replacesDelegationId);
|
|
142
|
+
if (resolution?.outcome !== 'not_owned') {
|
|
143
|
+
throw new WorkHubActionGateFailure('action_conflict', 'WorkHub delegation already has a stop claim');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
124
146
|
const header = await this.#stores.readHeaderSnapshot(WORKHUB_COORDINATION_SESSION_ID);
|
|
125
147
|
if (!validCoordinationHeader(header)) {
|
|
126
148
|
throw new WorkHubActionEffectFailure('operation_conflict', 'WorkHub Coordination Session identity is unavailable');
|
|
@@ -129,10 +151,105 @@ export class HostWorkHubCoordinationCoordinator {
|
|
|
129
151
|
unknownOutcomeMessage: 'WorkHub replacement intent outcome is unknown',
|
|
130
152
|
});
|
|
131
153
|
}
|
|
154
|
+
async #prepareStop(input) {
|
|
155
|
+
const suffix = workHubDestructiveClaimIdentitySuffix(input.stopsDelegationId);
|
|
156
|
+
return this.#commitCoordinationFact({
|
|
157
|
+
// Only the two Sessions this stop can change: the one whose delegation
|
|
158
|
+
// ends, and the Coordination Session that records it. Holding a lane for
|
|
159
|
+
// every Session with an active delegation would serialize unrelated
|
|
160
|
+
// delegation traffic behind one stop, and the proof below needs no lane
|
|
161
|
+
// it does not already hold.
|
|
162
|
+
admissionSessionIds: [WORKHUB_COORDINATION_SESSION_ID, input.targetSessionId],
|
|
163
|
+
read: () => this.#stores.readWorkHubStopRequest(input.stopsDelegationId),
|
|
164
|
+
build: (existing) => ({
|
|
165
|
+
type: 'workhub_coordination',
|
|
166
|
+
id: `whq_${suffix}`,
|
|
167
|
+
turnId: input.actionId,
|
|
168
|
+
ts: existing?.ts ?? Date.now(),
|
|
169
|
+
schemaVersion: WORKHUB_COORDINATION_STOP_SCHEMA_VERSION,
|
|
170
|
+
kind: 'delegation_stop_requested',
|
|
171
|
+
actionId: input.actionId,
|
|
172
|
+
actionFingerprint: input.actionFingerprint,
|
|
173
|
+
coordinationTurnId: input.actionId,
|
|
174
|
+
stopsActionId: input.stopsActionId,
|
|
175
|
+
stopsDelegationId: input.stopsDelegationId,
|
|
176
|
+
targetSessionId: input.targetSessionId,
|
|
177
|
+
targetMessageId: input.targetMessageId,
|
|
178
|
+
targetSessionName: input.targetSessionName,
|
|
179
|
+
userText: input.userText,
|
|
180
|
+
}),
|
|
181
|
+
conflictMessage: 'WorkHub delegation already has a different stop claim',
|
|
182
|
+
beforeAppend: async () => {
|
|
183
|
+
const [replacement, supersession, messages] = await Promise.all([
|
|
184
|
+
this.#stores.readWorkHubReplacement(input.stopsDelegationId),
|
|
185
|
+
this.#stores.readWorkHubSupersession(input.stopsDelegationId),
|
|
186
|
+
this.#stores.readMessagesSnapshot(WORKHUB_COORDINATION_SESSION_ID),
|
|
187
|
+
]);
|
|
188
|
+
if (replacement || supersession) {
|
|
189
|
+
throw new WorkHubActionGateFailure('action_conflict', 'WorkHub delegation is already being replaced');
|
|
190
|
+
}
|
|
191
|
+
const activeAssignments = activeWorkHubAssignments(messages);
|
|
192
|
+
// Held lanes make this the last moment the one-target proof can change.
|
|
193
|
+
// It is proved from opaque delegation identity, so a concurrent rename
|
|
194
|
+
// is harmless while a concurrent delegation to the same Session is not.
|
|
195
|
+
const targetActive = activeAssignments.filter((assignment) => assignment.targetSessionId === input.targetSessionId);
|
|
196
|
+
const source = targetActive.find((assignment) => assignment.actionId === input.stopsActionId &&
|
|
197
|
+
assignment.delegationId === input.stopsDelegationId);
|
|
198
|
+
if (!source) {
|
|
199
|
+
throw new WorkHubActionGateFailure('action_conflict', 'WorkHub stop target does not identify one active durable delegation');
|
|
200
|
+
}
|
|
201
|
+
// A delegation whose work already finished stays linked but competes
|
|
202
|
+
// for nothing; only work that could still be stopped makes the target
|
|
203
|
+
// ambiguous.
|
|
204
|
+
for (const competitor of targetActive) {
|
|
205
|
+
if (competitor.delegationId === source.delegationId)
|
|
206
|
+
continue;
|
|
207
|
+
if ((await this.#readDelegationRetirement(competitor)) !== 'retired') {
|
|
208
|
+
throw new WorkHubActionGateFailure('action_conflict', 'WorkHub stop target does not identify one active durable delegation');
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
unknownOutcomeMessage: 'WorkHub stop request outcome is unknown',
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
async #listActiveAssignments() {
|
|
216
|
+
return activeWorkHubAssignments(await this.#stores.readMessagesSnapshot(WORKHUB_COORDINATION_SESSION_ID));
|
|
217
|
+
}
|
|
218
|
+
#resolveStop(input) {
|
|
219
|
+
const request = input.request;
|
|
220
|
+
const suffix = workHubDestructiveClaimIdentitySuffix(request.stopsDelegationId);
|
|
221
|
+
return this.#commitCoordinationFact({
|
|
222
|
+
read: () => this.#stores.readWorkHubStopResolution(request.stopsDelegationId),
|
|
223
|
+
build: (existing) => ({
|
|
224
|
+
type: 'workhub_coordination',
|
|
225
|
+
id: `whz_${suffix}`,
|
|
226
|
+
turnId: request.actionId,
|
|
227
|
+
ts: existing?.ts ?? Date.now(),
|
|
228
|
+
schemaVersion: WORKHUB_COORDINATION_STOP_SCHEMA_VERSION,
|
|
229
|
+
kind: 'delegation_stop_resolved',
|
|
230
|
+
actionId: request.actionId,
|
|
231
|
+
actionFingerprint: request.actionFingerprint,
|
|
232
|
+
coordinationTurnId: request.coordinationTurnId,
|
|
233
|
+
stopsActionId: request.stopsActionId,
|
|
234
|
+
stopsDelegationId: request.stopsDelegationId,
|
|
235
|
+
targetSessionId: request.targetSessionId,
|
|
236
|
+
outcome: input.outcome,
|
|
237
|
+
...(input.targetTurnId ? { targetTurnId: input.targetTurnId } : {}),
|
|
238
|
+
}),
|
|
239
|
+
conflictMessage: 'WorkHub stop already has a different resolution',
|
|
240
|
+
beforeAppend: async () => {
|
|
241
|
+
const durable = await this.#stores.readWorkHubStopRequest(request.stopsDelegationId);
|
|
242
|
+
if (!durable || !isDeepStrictEqual(durable, request)) {
|
|
243
|
+
throw new WorkHubActionGateFailure('action_conflict', 'WorkHub stop request identity changed');
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
unknownOutcomeMessage: 'WorkHub stop resolution outcome is unknown',
|
|
247
|
+
});
|
|
248
|
+
}
|
|
132
249
|
#abortReplacement(input) {
|
|
133
250
|
const replacement = input.replacement;
|
|
134
|
-
const suffix =
|
|
135
|
-
return this.#
|
|
251
|
+
const suffix = workHubDestructiveClaimIdentitySuffix(replacement.replacesDelegationId);
|
|
252
|
+
return this.#commitCoordinationFact({
|
|
136
253
|
read: () => this.#stores.readWorkHubReplacementAbort(replacement.replacesDelegationId),
|
|
137
254
|
build: (existing) => ({
|
|
138
255
|
type: 'workhub_coordination',
|
|
@@ -159,8 +276,8 @@ export class HostWorkHubCoordinationCoordinator {
|
|
|
159
276
|
unknownOutcomeMessage: 'WorkHub replacement abort outcome is unknown',
|
|
160
277
|
});
|
|
161
278
|
}
|
|
162
|
-
#
|
|
163
|
-
return this.#admission.
|
|
279
|
+
#commitCoordinationFact(options) {
|
|
280
|
+
return this.#admission.runMany(options.admissionSessionIds ?? [WORKHUB_COORDINATION_SESSION_ID], async (lease) => {
|
|
164
281
|
const existing = await options.read();
|
|
165
282
|
const requested = options.build(existing);
|
|
166
283
|
if (existing) {
|
|
@@ -445,7 +562,28 @@ function validCoordinationHeader(header) {
|
|
|
445
562
|
function digest(value) {
|
|
446
563
|
return `sha256:${createHash('sha256').update(JSON.stringify(value)).digest('hex')}`;
|
|
447
564
|
}
|
|
448
|
-
function
|
|
565
|
+
function activeWorkHubAssignments(messages) {
|
|
566
|
+
const terminalDelegationIds = new Set();
|
|
567
|
+
const assignments = [];
|
|
568
|
+
for (const message of messages) {
|
|
569
|
+
if (message.type !== 'workhub_coordination')
|
|
570
|
+
continue;
|
|
571
|
+
if (message.kind === 'delegation_assigned') {
|
|
572
|
+
assignments.push(message);
|
|
573
|
+
}
|
|
574
|
+
else if (message.kind === 'delegation_superseded') {
|
|
575
|
+
terminalDelegationIds.add(message.supersededDelegationId);
|
|
576
|
+
}
|
|
577
|
+
else if (message.kind === 'delegation_replacement_aborted') {
|
|
578
|
+
terminalDelegationIds.add(message.abortedDelegationId);
|
|
579
|
+
}
|
|
580
|
+
else if (message.kind === 'delegation_stop_resolved' && message.outcome !== 'not_owned') {
|
|
581
|
+
terminalDelegationIds.add(message.stopsDelegationId);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return assignments.filter(({ delegationId }) => !terminalDelegationIds.has(delegationId));
|
|
585
|
+
}
|
|
586
|
+
function workHubDestructiveClaimIdentitySuffix(delegationId) {
|
|
449
587
|
return createHash('sha256').update(delegationId, 'utf8').digest('hex').slice(0, 48);
|
|
450
588
|
}
|
|
451
589
|
function coordinationSummaryMessageId(turnId, kind) {
|
|
@@ -69,6 +69,7 @@ export function startRuntimeHostPeerEndpoint(input) {
|
|
|
69
69
|
try {
|
|
70
70
|
const endpoint = loadNativeModule(input.nativePath).startPeerEndpoint({
|
|
71
71
|
keyPath: input.keyPath,
|
|
72
|
+
...(input.relayAnchorPath ? { relayAnchorPath: input.relayAnchorPath } : {}),
|
|
72
73
|
...(input.expectedPeerId ? { expectedPeerId: input.expectedPeerId } : {}),
|
|
73
74
|
...(input.listenAddresses ? { listenAddresses: input.listenAddresses } : {}),
|
|
74
75
|
...(input.coordinationRelays ? { coordinationRelays: input.coordinationRelays } : {}),
|
|
@@ -281,20 +282,24 @@ function isPeerNativeEndpoint(value) {
|
|
|
281
282
|
value !== null &&
|
|
282
283
|
'peerId' in value &&
|
|
283
284
|
isPeerId(value.peerId) &&
|
|
284
|
-
'
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
Array.isArray(value.activeCoordinationRelays) &&
|
|
289
|
-
value.activeCoordinationRelays.every((address) => typeof address === 'string') &&
|
|
285
|
+
'reachabilitySnapshot' in value &&
|
|
286
|
+
isPeerReachabilitySnapshot(value.reachabilitySnapshot) &&
|
|
287
|
+
'connectivitySnapshot' in value &&
|
|
288
|
+
isPeerConnectivitySnapshot(value.connectivitySnapshot) &&
|
|
290
289
|
'transitSnapshot' in value &&
|
|
291
290
|
isPeerTransitSnapshot(value.transitSnapshot) &&
|
|
292
291
|
'connect' in value &&
|
|
293
292
|
typeof value.connect === 'function' &&
|
|
294
293
|
'connectMeshControl' in value &&
|
|
295
294
|
typeof value.connectMeshControl === 'function' &&
|
|
295
|
+
'updateConnect' in value &&
|
|
296
|
+
typeof value.updateConnect === 'function' &&
|
|
296
297
|
'configureTransit' in value &&
|
|
297
298
|
typeof value.configureTransit === 'function' &&
|
|
299
|
+
'watchReachability' in value &&
|
|
300
|
+
typeof value.watchReachability === 'function' &&
|
|
301
|
+
'watchConnectivity' in value &&
|
|
302
|
+
typeof value.watchConnectivity === 'function' &&
|
|
298
303
|
'cancelConnect' in value &&
|
|
299
304
|
typeof value.cancelConnect === 'function' &&
|
|
300
305
|
'accept' in value &&
|
|
@@ -304,6 +309,27 @@ function isPeerNativeEndpoint(value) {
|
|
|
304
309
|
'close' in value &&
|
|
305
310
|
typeof value.close === 'function');
|
|
306
311
|
}
|
|
312
|
+
function isPeerConnectivitySnapshot(value) {
|
|
313
|
+
return (typeof value === 'object' &&
|
|
314
|
+
value !== null &&
|
|
315
|
+
'generation' in value &&
|
|
316
|
+
isCount(value.generation) &&
|
|
317
|
+
'connectedPeerIds' in value &&
|
|
318
|
+
Array.isArray(value.connectedPeerIds) &&
|
|
319
|
+
value.connectedPeerIds.every((peerId) => typeof peerId === 'string'));
|
|
320
|
+
}
|
|
321
|
+
function isPeerReachabilitySnapshot(value) {
|
|
322
|
+
return (typeof value === 'object' &&
|
|
323
|
+
value !== null &&
|
|
324
|
+
'generation' in value &&
|
|
325
|
+
isCount(value.generation) &&
|
|
326
|
+
'listenAddresses' in value &&
|
|
327
|
+
Array.isArray(value.listenAddresses) &&
|
|
328
|
+
value.listenAddresses.every((address) => typeof address === 'string') &&
|
|
329
|
+
'activeCoordinationRelays' in value &&
|
|
330
|
+
Array.isArray(value.activeCoordinationRelays) &&
|
|
331
|
+
value.activeCoordinationRelays.every((address) => typeof address === 'string'));
|
|
332
|
+
}
|
|
307
333
|
function isPeerTransitSnapshot(value) {
|
|
308
334
|
return (typeof value === 'object' &&
|
|
309
335
|
value !== null &&
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"./protocol": "./dist/protocol/index.js",
|
|
10
10
|
"./client": "./dist/client/index.js",
|
|
11
11
|
"./webrtc-stun-policy": "./dist/webrtc-stun-policy.js",
|
|
12
|
+
"./peer-reachability": "./dist/peer-reachability/index.js",
|
|
12
13
|
"./peer-mesh": "./dist/peer-mesh/index.js",
|
|
13
14
|
"./operator": "./dist/operator/index.js",
|
|
14
15
|
"./operator/update-package-evidence": "./dist/operator/update-package-evidence.js",
|
|
@@ -1249,8 +1249,10 @@ function assertRootTurnAdmissionContract(admission) {
|
|
|
1249
1249
|
if (admission.skillInvocation && execution.kind !== 'external_message') {
|
|
1250
1250
|
throw new Error('Invalid root turn admission contract: Skill invocation requires external message execution');
|
|
1251
1251
|
}
|
|
1252
|
-
if (admission.authorization &&
|
|
1253
|
-
|
|
1252
|
+
if (admission.authorization &&
|
|
1253
|
+
execution.kind !== 'external_message' &&
|
|
1254
|
+
execution.kind !== 'regenerate') {
|
|
1255
|
+
throw new Error('Invalid root turn admission contract: authorization proof requires external message or regenerate execution');
|
|
1254
1256
|
}
|
|
1255
1257
|
if (execution.kind === 'claimed_agent_graph_intent') {
|
|
1256
1258
|
if (execution.claim.targetSessionId !== admission.sessionId ||
|
|
@@ -1410,12 +1412,23 @@ function normalizeRootExecutionDescriptor(value) {
|
|
|
1410
1412
|
return Object.freeze({ kind: 'context_compact' });
|
|
1411
1413
|
}
|
|
1412
1414
|
if (value.kind === 'scheduled_task') {
|
|
1413
|
-
if (!hasExactKeys(value, [
|
|
1415
|
+
if (!hasExactKeys(value, [
|
|
1416
|
+
'kind',
|
|
1417
|
+
'scheduledTaskId',
|
|
1418
|
+
...(Object.hasOwn(value, 'executionFingerprint') ? ['executionFingerprint'] : []),
|
|
1419
|
+
]) ||
|
|
1414
1420
|
typeof value.scheduledTaskId !== 'string' ||
|
|
1415
|
-
!isSafeId(value.scheduledTaskId)
|
|
1421
|
+
!isSafeId(value.scheduledTaskId) ||
|
|
1422
|
+
(value.executionFingerprint !== undefined && !isSha256Digest(value.executionFingerprint))) {
|
|
1416
1423
|
throw new Error('Invalid root execution descriptor');
|
|
1417
1424
|
}
|
|
1418
|
-
return Object.freeze({
|
|
1425
|
+
return Object.freeze({
|
|
1426
|
+
kind: 'scheduled_task',
|
|
1427
|
+
scheduledTaskId: value.scheduledTaskId,
|
|
1428
|
+
...(value.executionFingerprint !== undefined
|
|
1429
|
+
? { executionFingerprint: value.executionFingerprint }
|
|
1430
|
+
: {}),
|
|
1431
|
+
});
|
|
1419
1432
|
}
|
|
1420
1433
|
if (value.kind === 'automation' || value.kind === 'legacy_automation') {
|
|
1421
1434
|
if (!hasExactKeys(value, ['kind', 'automationId']) ||
|
|
@@ -260,6 +260,7 @@ function convertCodexRollout(text, expectedSessionId, fallbackName, fallbackCwd)
|
|
|
260
260
|
const text = codexCompletedItemText(item);
|
|
261
261
|
if (text.length === 0)
|
|
262
262
|
continue;
|
|
263
|
+
const providerOptions = codexAssistantProviderOptions(item);
|
|
263
264
|
messages.push({
|
|
264
265
|
type: 'assistant',
|
|
265
266
|
id: stringField(item, 'id') ??
|
|
@@ -267,6 +268,7 @@ function convertCodexRollout(text, expectedSessionId, fallbackName, fallbackCwd)
|
|
|
267
268
|
turnId: ensureTurnId(record.line),
|
|
268
269
|
ts: timestampFor(record),
|
|
269
270
|
text,
|
|
271
|
+
...(providerOptions !== undefined ? { providerOptions } : {}),
|
|
270
272
|
modelId: activeModel,
|
|
271
273
|
contentOrder: ['text'],
|
|
272
274
|
});
|
|
@@ -313,12 +315,14 @@ function convertCodexRollout(text, expectedSessionId, fallbackName, fallbackCwd)
|
|
|
313
315
|
const text = stringField(payload, 'message');
|
|
314
316
|
if (!text)
|
|
315
317
|
continue;
|
|
318
|
+
const providerOptions = codexAssistantProviderOptions(payload);
|
|
316
319
|
messages.push({
|
|
317
320
|
type: 'assistant',
|
|
318
321
|
id: generatedCodexId(expectedSessionId, 'assistant', record.line),
|
|
319
322
|
turnId: ensureTurnId(record.line),
|
|
320
323
|
ts: timestampFor(record),
|
|
321
324
|
text,
|
|
325
|
+
...(providerOptions !== undefined ? { providerOptions } : {}),
|
|
322
326
|
modelId: activeModel,
|
|
323
327
|
contentOrder: ['text'],
|
|
324
328
|
});
|
|
@@ -698,6 +702,16 @@ function stringField(record, field) {
|
|
|
698
702
|
const value = record?.[field];
|
|
699
703
|
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
700
704
|
}
|
|
705
|
+
function codexAssistantProviderOptions(record) {
|
|
706
|
+
const phase = record?.phase;
|
|
707
|
+
if (phase !== 'commentary' && phase !== 'final_answer')
|
|
708
|
+
return undefined;
|
|
709
|
+
return {
|
|
710
|
+
openai: {
|
|
711
|
+
phase,
|
|
712
|
+
},
|
|
713
|
+
};
|
|
714
|
+
}
|
|
701
715
|
function isSafeCodexSessionId(value) {
|
|
702
716
|
return typeof value === 'string' && CODEX_SESSION_ID_PATTERN.test(value);
|
|
703
717
|
}
|
|
@@ -127,6 +127,10 @@ async function createExecutionStoresForWrite(lease, kind, extension) {
|
|
|
127
127
|
readWorkHubReplacement: (delegationId) => run(() => sessionStore.readWorkHubReplacement(delegationId)),
|
|
128
128
|
readWorkHubReplacementAbort: (delegationId) => run(() => sessionStore.readWorkHubReplacementAbort(delegationId)),
|
|
129
129
|
readWorkHubSupersession: (delegationId) => run(() => sessionStore.readWorkHubSupersession(delegationId)),
|
|
130
|
+
readWorkHubStopRequest: (delegationId) => run(() => sessionStore.readWorkHubStopRequest(delegationId)),
|
|
131
|
+
readWorkHubStopResolution: (delegationId) => run(() => sessionStore.readWorkHubStopResolution(delegationId)),
|
|
132
|
+
claimWorkHubAction: (claim) => run(() => sessionStore.claimWorkHubAction(claim)),
|
|
133
|
+
readWorkHubActionClaim: (actionId) => run(() => sessionStore.readWorkHubActionClaim(actionId)),
|
|
130
134
|
discardStableConversationCopy: (sessionId, requestFingerprint) => run(() => sessionStore.discardStableConversationCopy(sessionId, requestFingerprint)),
|
|
131
135
|
createSubagent: (input, initialBoundary) => run(() => sessionStore.createSubagent(input, initialBoundary)),
|
|
132
136
|
createAgentGraphOperator: (input, request, expectedRevision, initialBoundary) => run(() => sessionStore.createAgentGraphOperator(input, request, expectedRevision, initialBoundary)),
|
|
@@ -162,6 +166,7 @@ async function createExecutionStoresForWrite(lease, kind, extension) {
|
|
|
162
166
|
commitMessageAdmission: (admission) => run(() => sessionStore.commitMessageAdmission(admission)),
|
|
163
167
|
readMessageAdmission: (sessionId, messageId) => run(() => sessionStore.readMessageAdmission(sessionId, messageId)),
|
|
164
168
|
hasCancelledMessageAdmission: (sessionId, messageId) => run(() => sessionStore.hasCancelledMessageAdmission(sessionId, messageId)),
|
|
169
|
+
claimMessageAdmissionCancellation: (sessionId, messageId, claimId) => run(() => sessionStore.claimMessageAdmissionCancellation(sessionId, messageId, claimId)),
|
|
165
170
|
listMessageAdmissions: (sessionId) => run(() => sessionStore.listMessageAdmissions(sessionId)),
|
|
166
171
|
markMessagesHandedOff: (input) => run(() => sessionStore.markMessagesHandedOff(input)),
|
|
167
172
|
updateMessageAdmission: (admission) => run(() => sessionStore.updateMessageAdmission(admission)),
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import { resolve } from 'node:path';
|
|
20
20
|
import { isDeepStrictEqual } from 'node:util';
|
|
21
21
|
import { clientCapabilityScopeIdentity, decodeClientCapabilitySessionGrant, decodeClientCapabilitySessionGrantKey, } from '@maka/core/client-capability-grant';
|
|
22
|
-
import { decodeInteractionCanonicalOutcome, decodeInteractionRequest, interactionCanonicalOutcomesEquivalent, isInteractionCanonicalOutcomeValidForRequest, projectInteractionQuestionRequest, } from '@maka/core/interaction';
|
|
22
|
+
import { decodeInteractionCanonicalOutcome, decodeInteractionRequest, interactionCanonicalOutcomesEquivalent, isInteractionCanonicalOutcomeValidForRequest, projectInteractionFormRequest, projectInteractionQuestionRequest, } from '@maka/core/interaction';
|
|
23
23
|
import { assertStorageRootLease, runWithStorageRootLease, StorageRootAuthorityError, } from './root-authority.js';
|
|
24
24
|
import { acquireOperationalStateDatabase, } from './operational-state-store.js';
|
|
25
25
|
const SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;
|
|
@@ -420,6 +420,18 @@ function normalizeRequest(value, source) {
|
|
|
420
420
|
decodeFailure(source, 'Interaction question request is not canonical safe text');
|
|
421
421
|
request = canonical;
|
|
422
422
|
}
|
|
423
|
+
else if (request.kind === 'form') {
|
|
424
|
+
const canonical = projectInteractionFormRequest({
|
|
425
|
+
toolUseId: request.toolUseId,
|
|
426
|
+
message: request.message,
|
|
427
|
+
requester: request.requester,
|
|
428
|
+
fields: request.fields,
|
|
429
|
+
});
|
|
430
|
+
if (!isDeepStrictEqual(request, canonical)) {
|
|
431
|
+
decodeFailure(source, 'Interaction form request is not canonical');
|
|
432
|
+
}
|
|
433
|
+
request = canonical;
|
|
434
|
+
}
|
|
423
435
|
}
|
|
424
436
|
catch (error) {
|
|
425
437
|
if (error instanceof InteractionStoreError)
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { randomUUID } from 'node:crypto';
|
|
20
20
|
import { isDeepStrictEqual } from 'node:util';
|
|
21
|
-
import { CONNECTION_CATALOG_MAX_CONNECTIONS, decodeCanonicalConnectionCatalogEntry, decodeConnectionSlug, decodeConnectionTarget, decodeConnectionTestSummary, decodeProviderType, decodeRuntimePolicyEntityId, normalizeConnectionCatalogEntryUpdateForProvider, normalizeConnectionModelDiscoveryResult, normalizeCreateCatalogConnectionInput, normalizeRemoveCatalogConnectionInput, normalizeSetDefaultConnectionTargetInput, normalizeUpdateCatalogConnectionInput, } from '@maka/core/runtime-policy';
|
|
21
|
+
import { CONNECTION_CATALOG_MAX_CONNECTIONS, decodeCanonicalConnectionCatalogEntry, decodeConnectionName, decodeConnectionSlug, decodeConnectionTarget, decodeConnectionTestSummary, decodeProviderType, decodeRuntimePolicyEntityId, normalizeConnectionCatalogEntryUpdateForProvider, normalizeConnectionModelDiscoveryResult, normalizeCreateCatalogConnectionInput, normalizeRemoveCatalogConnectionInput, normalizeSetDefaultConnectionTargetInput, normalizeUpdateCatalogConnectionInput, } from '@maka/core/runtime-policy';
|
|
22
22
|
import { PROVIDER_REGISTRY, reconcileConnectionAfterModelFetch } from '@maka/core/llm-connections';
|
|
23
23
|
import { modelIdAliasesForProvider } from '@maka/core/model-metadata';
|
|
24
24
|
import { isRetiredProvider } from '@maka/core/provider-registry';
|
|
@@ -354,10 +354,11 @@ export class ConnectionCatalogDocumentOwner {
|
|
|
354
354
|
const { lastTest: _lastTest, ...discoveredWithoutLastTest } = discovered;
|
|
355
355
|
return this.writePatchedResult(root, current, index, testBasisChanged ? discoveredWithoutLastTest : discovered, defaultTarget);
|
|
356
356
|
}
|
|
357
|
-
prepareOnboardingUpsert(current, rawConnectionId, rawSlug, rawProviderType, rawBaseUrl, rawEnabledModelIds, rawResult, invalidateLastTest) {
|
|
357
|
+
prepareOnboardingUpsert(current, rawConnectionId, rawSlug, rawProviderType, rawName, rawBaseUrl, rawEnabledModelIds, rawResult, invalidateLastTest) {
|
|
358
358
|
const connectionId = decodeConnectionInput(() => decodeRuntimePolicyEntityId(rawConnectionId));
|
|
359
359
|
const slug = decodeConnectionInput(() => decodeConnectionSlug(rawSlug));
|
|
360
360
|
const providerType = decodeConnectionInput(() => decodeProviderType(rawProviderType));
|
|
361
|
+
const requestedName = rawName === null ? null : decodeConnectionInput(() => decodeConnectionName(rawName));
|
|
361
362
|
const definition = PROVIDER_REGISTRY[providerType];
|
|
362
363
|
// Identity first: the intent's connectionId names the connection being
|
|
363
364
|
// edited, whatever slug it lives under — a relay created in Desktop under
|
|
@@ -389,7 +390,9 @@ export class ConnectionCatalogDocumentOwner {
|
|
|
389
390
|
// existing override or the registry default (blank-reuse, like the key).
|
|
390
391
|
const effectiveBaseUrl = rawBaseUrl ?? previous?.baseUrl ?? definition.baseUrl;
|
|
391
392
|
const changes = decodeConnectionInput(() => normalizeConnectionCatalogEntryUpdateForProvider({
|
|
392
|
-
|
|
393
|
+
// An edit keeps the stored name; a create takes the caller's choice
|
|
394
|
+
// when the wizard collected one, else the provider label.
|
|
395
|
+
name: previous?.name ?? requestedName ?? definition.label,
|
|
393
396
|
...(effectiveBaseUrl ? { baseUrl: effectiveBaseUrl } : {}),
|
|
394
397
|
enabled: true,
|
|
395
398
|
enabledModelIds: rawEnabledModelIds,
|
|
@@ -427,6 +430,9 @@ export class ConnectionCatalogDocumentOwner {
|
|
|
427
430
|
const { relayModelProfiles: _staleProfiles, ...baseWithoutProfiles } = base;
|
|
428
431
|
const finalized = {
|
|
429
432
|
...baseWithoutProfiles,
|
|
433
|
+
// `changes.name` carries the edit-preserving / create-requested / provider
|
|
434
|
+
// -label resolution — `base` alone would keep the provider label forever.
|
|
435
|
+
name: changes.name,
|
|
430
436
|
...(relayModelProfiles ? { relayModelProfiles } : {}),
|
|
431
437
|
...(changes.baseUrl !== undefined ? { baseUrl: changes.baseUrl } : {}),
|
|
432
438
|
revision: previous ? nextRevision(previous.revision) : 1,
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { randomUUID } from 'node:crypto';
|
|
20
20
|
import { isDeepStrictEqual } from 'node:util';
|
|
21
|
-
import { CONNECTION_CATALOG_MAX_CONNECTIONS, decodeConnectionModelId, connectionCredentialTarget, decodeConnectionCredentialTarget, decodeConnectionSlug, decodeProviderType, decodeRuntimePolicyEntityId, decodeCredentialLocator, normalizeDeleteCredentialInput, normalizeRemoveCatalogConnectionInput, normalizeRequestHeaderUpdates, normalizeRequestHeaders, normalizeSetCredentialInput, parseRequestHeaders, serializeRequestHeaders, RequestCustomizationValidationError, normalizeCredentialSecret, normalizeCatalogConnectionBaseUrl, normalizeNetworkProxyUpdate, networkProxyCredentialTarget, } from '@maka/core/runtime-policy';
|
|
21
|
+
import { CONNECTION_CATALOG_MAX_CONNECTIONS, decodeConnectionModelId, connectionCredentialTarget, decodeConnectionCredentialTarget, decodeConnectionName, decodeConnectionSlug, decodeProviderType, decodeRuntimePolicyEntityId, decodeCredentialLocator, normalizeDeleteCredentialInput, normalizeRemoveCatalogConnectionInput, normalizeRequestHeaderUpdates, normalizeRequestHeaders, normalizeSetCredentialInput, parseRequestHeaders, serializeRequestHeaders, RequestCustomizationValidationError, normalizeCredentialSecret, normalizeCatalogConnectionBaseUrl, normalizeNetworkProxyUpdate, networkProxyCredentialTarget, } from '@maka/core/runtime-policy';
|
|
22
22
|
import { applyModelFactOverridesToConnection, applyModelFactOverridesToCatalogSnapshot, } from '@maka/core/model-facts';
|
|
23
23
|
import { deriveProviderAuthContract } from '@maka/core/provider-auth';
|
|
24
24
|
import { isRetiredProvider } from '@maka/core/provider-registry';
|
|
@@ -794,13 +794,21 @@ export class RuntimePolicyCoordinator {
|
|
|
794
794
|
const requestedTarget = input.target;
|
|
795
795
|
if (requestedTarget.kind === 'create') {
|
|
796
796
|
const providerType = decodeConnectionInput(() => decodeProviderType(requestedTarget.providerType));
|
|
797
|
+
const requestedSlug = requestedTarget.slug === undefined
|
|
798
|
+
? null
|
|
799
|
+
: decodeConnectionInput(() => decodeConnectionSlug(requestedTarget.slug));
|
|
797
800
|
target = {
|
|
798
801
|
kind: 'create',
|
|
799
802
|
candidate: {
|
|
800
803
|
connectionId: randomUUID(),
|
|
801
|
-
slug:
|
|
804
|
+
slug: requestedSlug ??
|
|
805
|
+
deriveConnectionSlug(providerType, catalog.connections.map((connection) => connection.slug)),
|
|
802
806
|
providerType,
|
|
803
807
|
},
|
|
808
|
+
slugRequested: requestedSlug !== null,
|
|
809
|
+
name: requestedTarget.name === undefined
|
|
810
|
+
? null
|
|
811
|
+
: decodeConnectionInput(() => decodeConnectionName(requestedTarget.name)),
|
|
804
812
|
};
|
|
805
813
|
}
|
|
806
814
|
else if (requestedTarget.kind === 'existing') {
|
|
@@ -822,16 +830,22 @@ export class RuntimePolicyCoordinator {
|
|
|
822
830
|
throw codecError('invalid_connection_input', 'Unknown connection onboarding target');
|
|
823
831
|
}
|
|
824
832
|
const providerType = target.candidate.providerType;
|
|
825
|
-
// Onboarding
|
|
826
|
-
//
|
|
827
|
-
//
|
|
828
|
-
if (!providerAuthSupportsApiKey(providerType)
|
|
833
|
+
// Onboarding may adopt either an API key or canonical serialized OAuth
|
|
834
|
+
// material. Providers without a connection credential slot have no
|
|
835
|
+
// business here (the Host applies the same gate before discovery).
|
|
836
|
+
if (!providerAuthSupportsApiKey(providerType) &&
|
|
837
|
+
PROVIDER_REGISTRY[providerType].authKind !== 'oauth_token') {
|
|
829
838
|
return deepFreeze({ kind: 'provider_unsupported' });
|
|
830
839
|
}
|
|
831
840
|
if (target.kind === 'create' &&
|
|
832
841
|
catalog.connections.length >= CONNECTION_CATALOG_MAX_CONNECTIONS) {
|
|
833
842
|
return deepFreeze({ kind: 'catalog_full' });
|
|
834
843
|
}
|
|
844
|
+
if (target.kind === 'create' &&
|
|
845
|
+
target.slugRequested &&
|
|
846
|
+
catalog.connections.some((connection) => connection.slug === target.candidate.slug)) {
|
|
847
|
+
return deepFreeze({ kind: 'slug_taken' });
|
|
848
|
+
}
|
|
835
849
|
const baseUrl = input.baseUrl === null
|
|
836
850
|
? null
|
|
837
851
|
: (decodeConnectionInput(() => normalizeCatalogConnectionBaseUrl(input.baseUrl, providerType)) ?? null);
|
|
@@ -910,6 +924,9 @@ export class RuntimePolicyCoordinator {
|
|
|
910
924
|
if (checked.kind === 'catalog_full') {
|
|
911
925
|
return deepFreeze({ kind: 'catalog_full' });
|
|
912
926
|
}
|
|
927
|
+
if (checked.kind === 'slug_taken') {
|
|
928
|
+
return deepFreeze({ kind: 'slug_taken' });
|
|
929
|
+
}
|
|
913
930
|
return deepFreeze(checked.kind === 'target_missing'
|
|
914
931
|
? { kind: 'target_missing' }
|
|
915
932
|
: { kind: 'superseded', changed: checked.changed });
|
|
@@ -943,6 +960,15 @@ export class RuntimePolicyCoordinator {
|
|
|
943
960
|
}
|
|
944
961
|
else if (catalog.connections.some((connection) => connection.connectionId === basis.target.candidate.connectionId ||
|
|
945
962
|
connection.slug === basis.target.candidate.slug)) {
|
|
963
|
+
// A caller-chosen slug losing the race is the caller's to fix, so it
|
|
964
|
+
// reports distinctly instead of as a generic basis change. A derived
|
|
965
|
+
// slug colliding still resolves by re-running the wizard, which simply
|
|
966
|
+
// derives again.
|
|
967
|
+
if (basis.target.slugRequested &&
|
|
968
|
+
catalog.connections.some((connection) => connection.slug === basis.target.candidate.slug &&
|
|
969
|
+
connection.connectionId !== basis.target.candidate.connectionId)) {
|
|
970
|
+
return { kind: 'slug_taken' };
|
|
971
|
+
}
|
|
946
972
|
changed.push('connection');
|
|
947
973
|
}
|
|
948
974
|
else if (catalog.connections.length >= CONNECTION_CATALOG_MAX_CONNECTIONS) {
|
|
@@ -974,11 +1000,10 @@ export class RuntimePolicyCoordinator {
|
|
|
974
1000
|
const connectionId = candidate.connectionId;
|
|
975
1001
|
let invalidateLastTest = false;
|
|
976
1002
|
if (input.suppliedSecret !== null) {
|
|
977
|
-
const locator =
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
};
|
|
1003
|
+
const locator = connectionCredentialLocator(connectionId, PROVIDER_REGISTRY[candidate.providerType].authKind);
|
|
1004
|
+
if (!locator) {
|
|
1005
|
+
throw codecError('invalid_document', 'Onboarding provider has no credential locator');
|
|
1006
|
+
}
|
|
982
1007
|
const vault = await this.vault.read(root);
|
|
983
1008
|
const credential = findCredential(vault, locator);
|
|
984
1009
|
if (credential?.secret !== input.suppliedSecret) {
|
|
@@ -1000,11 +1025,15 @@ export class RuntimePolicyCoordinator {
|
|
|
1000
1025
|
connectionId,
|
|
1001
1026
|
slug: candidate.slug,
|
|
1002
1027
|
providerType: candidate.providerType,
|
|
1028
|
+
name: basis.target.kind === 'create' ? basis.target.name : null,
|
|
1003
1029
|
baseUrl: basis.baseUrl,
|
|
1004
1030
|
invalidateLastTest,
|
|
1005
1031
|
});
|
|
1006
|
-
const catalogPreflight = this.catalog.prepareOnboardingUpsert(catalog, intent.connectionId, intent.slug, intent.providerType, intent.baseUrl, intent.enabledModelIds, intent.discovery, intent.invalidateLastTest);
|
|
1032
|
+
const catalogPreflight = this.catalog.prepareOnboardingUpsert(catalog, intent.connectionId, intent.slug, intent.providerType, intent.name, intent.baseUrl, intent.enabledModelIds, intent.discovery, intent.invalidateLastTest);
|
|
1007
1033
|
if (catalogPreflight.kind === 'slug_conflict') {
|
|
1034
|
+
if (basis.target.kind === 'create' && basis.target.slugRequested) {
|
|
1035
|
+
return deepFreeze({ kind: 'slug_taken' });
|
|
1036
|
+
}
|
|
1008
1037
|
return deepFreeze({ kind: 'superseded', changed: ['connection'] });
|
|
1009
1038
|
}
|
|
1010
1039
|
if (catalogPreflight.kind === 'catalog_full') {
|
|
@@ -1438,7 +1467,7 @@ export class RuntimePolicyCoordinator {
|
|
|
1438
1467
|
// Validate the durable identity and final catalog shape before touching
|
|
1439
1468
|
// the vault. A damaged v2 intent must not rotate a real connection's
|
|
1440
1469
|
// credential before discovering that its ID/slug pair cannot commit.
|
|
1441
|
-
const prepared = this.catalog.prepareOnboardingUpsert(catalog, intent.connectionId, slug, intent.providerType, intent.baseUrl, intent.enabledModelIds, intent.discovery, intent.invalidateLastTest);
|
|
1470
|
+
const prepared = this.catalog.prepareOnboardingUpsert(catalog, intent.connectionId, slug, intent.providerType, intent.name, intent.baseUrl, intent.enabledModelIds, intent.discovery, intent.invalidateLastTest);
|
|
1442
1471
|
if (prepared.kind === 'slug_conflict') {
|
|
1443
1472
|
throw codecError('invalid_document', intent.schemaVersion === 1
|
|
1444
1473
|
? 'Legacy onboarding intent conflicts with the connection id'
|
|
@@ -1448,11 +1477,10 @@ export class RuntimePolicyCoordinator {
|
|
|
1448
1477
|
throw codecError('invalid_document', 'Onboarding intent exceeds the connection catalog');
|
|
1449
1478
|
}
|
|
1450
1479
|
if (intent.suppliedSecret !== null) {
|
|
1451
|
-
const locator =
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
};
|
|
1480
|
+
const locator = connectionCredentialLocator(intent.connectionId, PROVIDER_REGISTRY[intent.providerType].authKind);
|
|
1481
|
+
if (!locator) {
|
|
1482
|
+
throw codecError('invalid_document', 'Onboarding provider has no credential locator');
|
|
1483
|
+
}
|
|
1456
1484
|
const vault = await this.vault.read(root);
|
|
1457
1485
|
const existing = findCredential(vault, locator);
|
|
1458
1486
|
if (existing?.secret !== intent.suppliedSecret) {
|
|
@@ -1696,7 +1724,9 @@ function requiresNetworkProxyCredential(networkProxy) {
|
|
|
1696
1724
|
return networkProxy.enabled && networkProxy.authEnabled;
|
|
1697
1725
|
}
|
|
1698
1726
|
function isInteractiveOAuthLoginProvider(providerType) {
|
|
1699
|
-
return providerType === 'openai-codex' ||
|
|
1727
|
+
return (providerType === 'openai-codex' ||
|
|
1728
|
+
providerType === 'xai-oauth' ||
|
|
1729
|
+
providerType === 'github-copilot');
|
|
1700
1730
|
}
|
|
1701
1731
|
function normalizeInteractiveOAuthLoginInput(input) {
|
|
1702
1732
|
const attemptId = decodeInteractiveOAuthAttemptId(input?.attemptId, 'invalid_connection_input');
|