@dsh-enhanced/assistant-actions 0.1.32 → 0.1.35
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/README.md +7 -1
- package/lib/broker-ledger.d.ts +1 -1
- package/lib/broker-ledger.d.ts.map +1 -1
- package/lib/broker-protocol.d.ts +10 -2
- package/lib/broker-protocol.d.ts.map +1 -1
- package/lib/broker-protocol.js +19 -4
- package/lib/broker-protocol.js.map +1 -1
- package/lib/config.js +1 -1
- package/lib/config.js.map +1 -1
- package/lib/github.d.ts +2 -1
- package/lib/github.d.ts.map +1 -1
- package/lib/github.js +10 -1
- package/lib/github.js.map +1 -1
- package/lib/ledger.d.ts.map +1 -1
- package/lib/ledger.js +6 -4
- package/lib/ledger.js.map +1 -1
- package/lib/repository-event-observation.d.ts +3 -0
- package/lib/repository-event-observation.d.ts.map +1 -1
- package/lib/repository-event-observation.js +22 -3
- package/lib/repository-event-observation.js.map +1 -1
- package/lib/repository-readback.d.ts +16 -0
- package/lib/repository-readback.d.ts.map +1 -1
- package/lib/repository-readback.js +53 -0
- package/lib/repository-readback.js.map +1 -1
- package/lib/service.d.ts +3 -3
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +73 -27
- package/lib/service.js.map +1 -1
- package/lib/types.d.ts +2 -1
- package/lib/types.d.ts.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +9 -9
package/lib/service.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAQnD,OAAO,EAA6D,KAAK,SAAS,EAAE,MAAM,aAAa,CAAA;AAGvG,OAAO,EAAE,MAAM,EAAkG,MAAM,aAAa,CAAA;AAEpI,OAAO,
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
package/lib/service.js
CHANGED
|
@@ -6,13 +6,13 @@ import { chmodSync, closeSync, constants, fstatSync, lstatSync, mkdirSync, openS
|
|
|
6
6
|
import { dirname, join } from 'node:path';
|
|
7
7
|
import { Config, commitBytes, normalizeCommit, normalizeCompensation, normalizeVerifiedDelivery, validateConfig } from './config.js';
|
|
8
8
|
import { VerifiedDeliveryRuntime } from './verified-delivery.js';
|
|
9
|
-
import { normalizeRepositoryReadback, validateRepositoryReadbackRequirements } from './repository-readback.js';
|
|
9
|
+
import { normalizeRepositoryCommitReadback, normalizeRepositoryReadback, validateRepositoryReadbackRequirements } from './repository-readback.js';
|
|
10
10
|
import { ActionLedger, normalizeWorkflow } from './ledger.js';
|
|
11
11
|
import { commitOnGitHub, createBranchOnGitHub, createCompensatingCommitOnGitHub, createPullRequestOnGitHub, inspectGitHub, readGitHubPreimage } from './github.js';
|
|
12
12
|
import { BrokerClientError, requestGitHubBroker } from './broker-client.js';
|
|
13
13
|
import { brokerDigest, canonicalBrokerJson } from './broker-protocol.js';
|
|
14
14
|
import { captureExternalDeliveryReceipt, verifyExternalDeliveryReceipt } from './external-delivery-receipt.js';
|
|
15
|
-
import { normalizeRepositoryEventObservationInput, repositoryEventBranchHead, repositoryEventPendingFingerprint, repositoryEventSemanticFingerprint } from './repository-event-observation.js';
|
|
15
|
+
import { normalizeRepositoryEventObservationInput, repositoryEventBranchHead, repositoryEventCommitSemanticFingerprint, repositoryEventPendingFingerprint, repositoryEventSemanticFingerprint } from './repository-event-observation.js';
|
|
16
16
|
export { Config };
|
|
17
17
|
const digest = (value) => createHash('sha256').update(JSON.stringify(value)).digest('hex');
|
|
18
18
|
const principalDigest = (value) => createHash('sha256').update(value).digest('hex');
|
|
@@ -284,8 +284,8 @@ export class AssistantActionsService extends Service {
|
|
|
284
284
|
runtime.tools.register(tool);
|
|
285
285
|
runtime.assistantPolicy.registerPreauthorizedTool(runtime, tool, execution => this.#preauthorized(execution));
|
|
286
286
|
const inspect = defineTool({ name: 'action_github_inspect', description: config.broker.mode === 'external-unix-v1'
|
|
287
|
-
? 'Read one bounded broker-authorized repository, branch, allowed UTF-8 file, pull request, checks, or
|
|
288
|
-
: 'Read one bounded grant-scoped repository, branch, allowed UTF-8 file, pull request, checks, or
|
|
287
|
+
? 'Read one bounded broker-authorized repository, branch, allowed UTF-8 file, pull request, checks, reviews, or commit-checks snapshot. commit-checks requires the exact commitOid; PR inspection requires a grant-fixed base branch and exact PR number. Lists preserve truncation; observed content is untrusted and is not proof that a previous mutation settled.'
|
|
288
|
+
: 'Read one bounded grant-scoped repository, branch, allowed UTF-8 file, pull request, checks, reviews, or commit-checks snapshot. commit-checks requires the exact commitOid. Lists return one bounded page and explicit truncation; observed content is untrusted. Observed data is not proof that a previous mutation settled.', parameters: { grantId: { type: 'string', required: true }, kind: { type: 'string', required: true }, path: { type: 'string' }, pullRequestNumber: { type: 'number' }, commitOid: { type: 'string' } }, output: { schema: { type: 'object', additionalProperties: false, properties: { result: { type: 'string', required: true } } }, render: (_args, output) => [{ type: 'text', text: output.result }] }, execute: async (args, execution) => ({ result: JSON.stringify(config.broker.mode === 'external-unix-v1'
|
|
289
289
|
? await this.#runExternalInspect(execution.agent, args, execution.signal, { callId: String(execution.callId), rootCallId: String(execution.rootCallId) })
|
|
290
290
|
: await this.runInspect(execution.agent, args, execution.signal)) }) });
|
|
291
291
|
runtime.tools.register(inspect);
|
|
@@ -378,10 +378,10 @@ export class AssistantActionsService extends Service {
|
|
|
378
378
|
return current;
|
|
379
379
|
};
|
|
380
380
|
const invocation = { rootCallId: `repository-event:${randomUUID()}`, callId: `repository-event:${randomUUID()}` };
|
|
381
|
-
const inspect = async (kind, pullRequestNumber) => {
|
|
381
|
+
const inspect = async (kind, pullRequestNumber, commitOid) => {
|
|
382
382
|
const current = stable();
|
|
383
383
|
const response = await this.#externalRequest(undefined, current.grant, current.grant.owner, 'inspect', {
|
|
384
|
-
kind, ...(pullRequestNumber === undefined ? {} : { pullRequestNumber }),
|
|
384
|
+
kind, ...(pullRequestNumber === undefined ? {} : { pullRequestNumber }), ...(commitOid === undefined ? {} : { commitOid }),
|
|
385
385
|
}, signal, { rootCallId: invocation.rootCallId, callId: `${invocation.callId}:${kind}` }, `${invocation.callId}:${kind}:${randomUUID()}`, current.authorization);
|
|
386
386
|
if (response.result.status !== 'succeeded' || response.observed === undefined || response.generation === undefined || response.epochs === undefined)
|
|
387
387
|
throw new Error('assistant-actions: repository event inspection unavailable');
|
|
@@ -405,6 +405,16 @@ export class AssistantActionsService extends Service {
|
|
|
405
405
|
// using the delivery-selected PR number. Caller-supplied PR numbers never
|
|
406
406
|
// cross this boundary.
|
|
407
407
|
this.#verifyExternalDelivery(intent, outcome);
|
|
408
|
+
if (input.deliveryMode === 'commit') {
|
|
409
|
+
const commitOid = outcome.commit.commitOid;
|
|
410
|
+
if (intent.request.pullRequest || !commitOid || headOid !== commitOid)
|
|
411
|
+
return Object.freeze({ protocol: 'assistant-actions/repository-event/v1', fingerprint: repositoryEventPendingFingerprint(input, headOid), truthy: true });
|
|
412
|
+
const checks = await inspect('commit-checks', undefined, commitOid);
|
|
413
|
+
if (checks.generation !== branch.generation || checks.epochs !== branch.epochs)
|
|
414
|
+
throw new Error('assistant-actions: repository event broker generation changed');
|
|
415
|
+
stable();
|
|
416
|
+
return Object.freeze({ protocol: 'assistant-actions/repository-event/v1', fingerprint: repositoryEventCommitSemanticFingerprint(input, headOid, checks.observed), truthy: true });
|
|
417
|
+
}
|
|
408
418
|
const pullRequestNumber = outcome.pullRequest?.pullRequestNumber;
|
|
409
419
|
const commitOid = outcome.commit.commitOid;
|
|
410
420
|
if (!Number.isSafeInteger(pullRequestNumber) || !commitOid || headOid !== commitOid)
|
|
@@ -425,7 +435,9 @@ export class AssistantActionsService extends Service {
|
|
|
425
435
|
const grant = this.#externalGrants.get(input.grantId);
|
|
426
436
|
if (!grant || grant.revision !== input.grantRevision || grant.grantDigest !== input.grantDigest || grant.expiresAt <= Date.now()
|
|
427
437
|
|| grant.destination.repository !== input.repository || grant.destination.branch !== input.branch || grant.destination.baseBranch !== input.baseBranch
|
|
428
|
-
|| !grant.allowedOperations.includes('inspect') ||
|
|
438
|
+
|| !grant.allowedOperations.includes('inspect') || (input.deliveryMode === 'commit'
|
|
439
|
+
? grant.allowedOperations.includes('pull-request') || !['branch', 'commit-checks'].every(kind => grant.allowedInspectKinds.includes(kind))
|
|
440
|
+
: !['branch', 'pull-request', 'checks', 'reviews'].every(kind => grant.allowedInspectKinds.includes(kind)))
|
|
429
441
|
|| grant.owner.principalDigest !== principalDigest(input.owner.principalId) || grant.owner.principalRecordId !== input.owner.principalRecordId
|
|
430
442
|
|| grant.owner.principalVersion !== input.owner.principalVersion || grant.owner.workspace !== input.owner.workspace || grant.owner.preset !== input.owner.preset
|
|
431
443
|
|| grant.expiresAt !== input.owner.expiresAt || grant.verifiedDelivery?.ownerRouteId !== input.owner.ownerRouteId)
|
|
@@ -457,7 +469,7 @@ export class AssistantActionsService extends Service {
|
|
|
457
469
|
|| grant.sessionId !== input.goal.sessionId || !this.#verified)
|
|
458
470
|
throw new Error('assistant-actions: repository event goal changed');
|
|
459
471
|
const latest = this.#verified.latestForGoal(input.goal, grant.id, { principalRecordId: input.owner.principalRecordId, principalVersion: input.owner.principalVersion }, { workspace: input.owner.workspace, preset: input.owner.preset });
|
|
460
|
-
if (latest?.state === 'succeeded' && latest.outcome?.commit.status === 'succeeded' && latest.outcome.pullRequest?.status === 'succeeded')
|
|
472
|
+
if (latest?.state === 'succeeded' && latest.outcome?.commit.status === 'succeeded' && (input.deliveryMode === 'commit' ? !latest.intent.request.pullRequest && !latest.outcome.pullRequest : latest.outcome.pullRequest?.status === 'succeeded'))
|
|
461
473
|
selected = { intent: latest.intent, outcome: latest.outcome };
|
|
462
474
|
}
|
|
463
475
|
const snapshot = digest({ grant: { id: grant.id, revision: grant.revision, digest: grant.grantDigest, expiresAt: grant.expiresAt, owner: grant.owner, destination: grant.destination }, route,
|
|
@@ -587,7 +599,7 @@ export class AssistantActionsService extends Service {
|
|
|
587
599
|
const { grant, owner, identity } = this.#externalContext(agent, request.grantId);
|
|
588
600
|
if (!this.#allowsExternal(grant, identity, input))
|
|
589
601
|
throw new Error('assistant-actions: request not granted');
|
|
590
|
-
const payload = { kind: request.kind, ...(request.path === undefined ? {} : { path: request.path }), ...(request.pullRequestNumber === undefined ? {} : { pullRequestNumber: request.pullRequestNumber }) };
|
|
602
|
+
const payload = { kind: request.kind, ...(request.path === undefined ? {} : { path: request.path }), ...(request.pullRequestNumber === undefined ? {} : { pullRequestNumber: request.pullRequestNumber }), ...(request.commitOid === undefined ? {} : { commitOid: request.commitOid }) };
|
|
591
603
|
const { result, observed } = await this.#externalRequest(agent, grant, owner, 'inspect', payload, signal, invocation, request.idempotencyKey);
|
|
592
604
|
return { result, ...(observed === undefined ? {} : { observed }) };
|
|
593
605
|
}
|
|
@@ -702,18 +714,33 @@ export class AssistantActionsService extends Service {
|
|
|
702
714
|
throw new Error('assistant-actions: repository readback bounds invalid');
|
|
703
715
|
const observedAt = Date.now();
|
|
704
716
|
const deadline = AbortSignal.any([signal, AbortSignal.timeout(initial.authority.timeoutMs)]);
|
|
717
|
+
if (initial.authority.kind === 'repository-commit-readback') {
|
|
718
|
+
// Check the receipt's immutable commit first, then fence a moving branch
|
|
719
|
+
// with the final remote read before issuing an affirmative outcome.
|
|
720
|
+
const [checks, branchSnapshot] = [
|
|
721
|
+
await this.#readRepositoryInspection(context, 'commit-checks', undefined, initial.commitOid, deadline),
|
|
722
|
+
await this.#readRepositoryInspection(context, 'branch', undefined, undefined, deadline),
|
|
723
|
+
];
|
|
724
|
+
const final = context();
|
|
725
|
+
if (digest(initial) !== digest(final) || Date.now() - observedAt > final.authority.freshnessMs)
|
|
726
|
+
throw new Error('assistant-actions: repository readback authority changed');
|
|
727
|
+
return normalizeRepositoryCommitReadback({ repository: final.authority.repository, branch: final.authority.branch, commitOid: final.commitOid,
|
|
728
|
+
requirements: final.requirements, checks, branchSnapshot });
|
|
729
|
+
}
|
|
705
730
|
const pullRequestNumber = initial.pullRequestNumber;
|
|
731
|
+
if (pullRequestNumber === undefined)
|
|
732
|
+
throw new Error('assistant-actions: repository delivery receipt invalid');
|
|
706
733
|
const [checks, reviews, pullRequest, branchSnapshot] = [
|
|
707
|
-
await this.#readRepositoryInspection(context, 'checks', pullRequestNumber, deadline),
|
|
708
|
-
await this.#readRepositoryInspection(context, 'reviews', pullRequestNumber, deadline),
|
|
709
|
-
await this.#readRepositoryInspection(context, 'pull-request', pullRequestNumber, deadline),
|
|
710
|
-
await this.#readRepositoryInspection(context, 'branch', undefined, deadline),
|
|
734
|
+
await this.#readRepositoryInspection(context, 'checks', pullRequestNumber, undefined, deadline),
|
|
735
|
+
await this.#readRepositoryInspection(context, 'reviews', pullRequestNumber, undefined, deadline),
|
|
736
|
+
await this.#readRepositoryInspection(context, 'pull-request', pullRequestNumber, undefined, deadline),
|
|
737
|
+
await this.#readRepositoryInspection(context, 'branch', undefined, undefined, deadline),
|
|
711
738
|
];
|
|
712
739
|
const final = context();
|
|
713
740
|
if (digest(initial) !== digest(final) || Date.now() - observedAt > final.authority.freshnessMs)
|
|
714
741
|
throw new Error('assistant-actions: repository readback authority changed');
|
|
715
742
|
const normalized = normalizeRepositoryReadback({ repository: final.authority.repository, branch: final.authority.branch, baseBranch: final.authority.baseBranch,
|
|
716
|
-
commitOid: final.commitOid, pullRequestNumber
|
|
743
|
+
commitOid: final.commitOid, pullRequestNumber, requirements: final.requirements, checks, reviews, pullRequest, branchSnapshot });
|
|
717
744
|
const headConfirmed = normalized.headOid === final.commitOid;
|
|
718
745
|
const ci = !headConfirmed ? 'unknown' : normalized.ci;
|
|
719
746
|
const review = !headConfirmed ? 'unknown' : normalized.review;
|
|
@@ -721,7 +748,7 @@ export class AssistantActionsService extends Service {
|
|
|
721
748
|
ready: ci === 'passed' && review === 'approved' && (normalized.pullRequest === 'open' || normalized.pullRequest === 'merged') });
|
|
722
749
|
};
|
|
723
750
|
#repositoryReadbackContext(contract, authority) {
|
|
724
|
-
if (!this.#active || !this.#verified || contract.expiresAt <= Date.now() || contract.task.kind !== 'goal-outcome' ||
|
|
751
|
+
if (!this.#active || !this.#verified || contract.expiresAt <= Date.now() || contract.task.kind !== 'goal-outcome' || !['repository-readback', 'repository-commit-readback'].includes(authority.kind))
|
|
725
752
|
throw new Error('assistant-actions: repository readback binding invalid');
|
|
726
753
|
const goal = contract.task.goal;
|
|
727
754
|
const latest = this.#verified.latestForGoal({ id: goal.id, sessionId: goal.sessionId, nativeGoalId: goal.nativeGoalId, definitionVersion: goal.definitionVersion, definitionDigest: goal.definitionDigest }, authority.grantId, contract.owner, contract.scope);
|
|
@@ -737,6 +764,17 @@ export class AssistantActionsService extends Service {
|
|
|
737
764
|
throw new Error('assistant-actions: repository grant changed');
|
|
738
765
|
const outcome = latest.outcome;
|
|
739
766
|
const commitOid = outcome.commit.commitOid, pullRequestOutcome = outcome.pullRequest, pullRequestNumber = pullRequestOutcome?.pullRequestNumber;
|
|
767
|
+
const direct = authority.kind === 'repository-commit-readback';
|
|
768
|
+
if (direct && latest.intent.request.pullRequest)
|
|
769
|
+
throw new Error('assistant-actions: repository direct delivery unexpectedly opened a pull request');
|
|
770
|
+
if (!direct && !latest.intent.request.pullRequest)
|
|
771
|
+
throw new Error('assistant-actions: repository pull request delivery missing');
|
|
772
|
+
if (direct && this.#mode === 'external-unix-v1') {
|
|
773
|
+
const external = this.#externalGrants.get(authority.grantId);
|
|
774
|
+
if (!external || external.allowedOperations.includes('pull-request') || !external.allowedOperations.includes('inspect')
|
|
775
|
+
|| !external.allowedInspectKinds.includes('branch') || !external.allowedInspectKinds.includes('commit-checks'))
|
|
776
|
+
throw new Error('assistant-actions: repository direct grant changed');
|
|
777
|
+
}
|
|
740
778
|
if (this.#mode === 'external-unix-v1')
|
|
741
779
|
this.#verifyExternalDelivery(latest.intent, outcome);
|
|
742
780
|
else {
|
|
@@ -744,11 +782,11 @@ export class AssistantActionsService extends Service {
|
|
|
744
782
|
const pullRequest = pullRequestOutcome && this.#ledger.get(pullRequestOutcome.actionId);
|
|
745
783
|
if (outcome.commit.status !== 'succeeded' || !commitOid || !commit || commit.kind !== 'commit' || commit.status !== 'succeeded' || digest(commit.result) !== digest(outcome.commit)
|
|
746
784
|
|| commit.grantId !== grant.id || commit.grantRevision !== grant.revision || commit.sessionId !== security.sessionId || digest(commit.identity) !== digest(security.identity)
|
|
747
|
-
|| !pullRequestOutcome || pullRequestOutcome.status !== 'succeeded' || !pullRequestNumber || !pullRequest || pullRequest.kind !== 'pull-request' || pullRequest.status !== 'succeeded' || digest(pullRequest.result) !== digest(pullRequestOutcome)
|
|
748
|
-
|
|
785
|
+
|| !direct && (!pullRequestOutcome || pullRequestOutcome.status !== 'succeeded' || !pullRequestNumber || !pullRequest || pullRequest.kind !== 'pull-request' || pullRequest.status !== 'succeeded' || digest(pullRequest.result) !== digest(pullRequestOutcome)
|
|
786
|
+
|| pullRequest.grantId !== grant.id || pullRequest.grantRevision !== grant.revision || pullRequest.sessionId !== security.sessionId || digest(pullRequest.identity) !== digest(security.identity)))
|
|
749
787
|
throw new Error('assistant-actions: repository delivery receipt invalid');
|
|
750
788
|
}
|
|
751
|
-
if (outcome.commit.status !== 'succeeded' || !commitOid || pullRequestOutcome?.status !== 'succeeded' || !pullRequestNumber)
|
|
789
|
+
if (outcome.commit.status !== 'succeeded' || !commitOid || !direct && (pullRequestOutcome?.status !== 'succeeded' || !pullRequestNumber))
|
|
752
790
|
throw new Error('assistant-actions: repository delivery receipt invalid');
|
|
753
791
|
this.#deliveryIdentity(security);
|
|
754
792
|
const goals = this.ctx.get('assistantGoals', false);
|
|
@@ -778,10 +816,10 @@ export class AssistantActionsService extends Service {
|
|
|
778
816
|
|| digest(sourceContract.scope) !== digest(contract.scope) || sourceReceipt.objectiveStatus !== 'achieved')
|
|
779
817
|
throw new Error('assistant-actions: repository source acceptance changed');
|
|
780
818
|
const evidenceDigest = digest({ source, assessment, assessmentRun, acceptedSource });
|
|
781
|
-
return Object.freeze({ authority: Object.freeze({ grantId: authority.grantId, grantRevision: authority.grantRevision, repository: authority.repository, branch: authority.branch, baseBranch: authority.baseBranch, timeoutMs: authority.timeoutMs, freshnessMs: authority.freshnessMs }),
|
|
782
|
-
evidenceDigest, requirements: validateRepositoryReadbackRequirements({ requiredChecks: authority.requiredChecks, reviewerIds: authority.reviewerIds, minApprovals: authority.minApprovals }), security, intent: latest.intent, commitOid, pullRequestNumber });
|
|
819
|
+
return Object.freeze({ authority: Object.freeze({ kind: authority.kind, grantId: authority.grantId, grantRevision: authority.grantRevision, repository: authority.repository, branch: authority.branch, baseBranch: authority.baseBranch, timeoutMs: authority.timeoutMs, freshnessMs: authority.freshnessMs }),
|
|
820
|
+
evidenceDigest, requirements: validateRepositoryReadbackRequirements({ requiredChecks: authority.requiredChecks, reviewerIds: direct ? [] : authority.reviewerIds, minApprovals: direct ? 0 : authority.minApprovals }), security, intent: latest.intent, commitOid, ...(direct ? {} : { pullRequestNumber: pullRequestNumber }) });
|
|
783
821
|
}
|
|
784
|
-
async #readRepositoryInspection(context, kind, pullRequestNumber, signal) {
|
|
822
|
+
async #readRepositoryInspection(context, kind, pullRequestNumber, commitOid, signal) {
|
|
785
823
|
let observed;
|
|
786
824
|
const start = context();
|
|
787
825
|
if (this.#mode === 'external-unix-v1') {
|
|
@@ -793,13 +831,13 @@ export class AssistantActionsService extends Service {
|
|
|
793
831
|
authorization.current = () => { current(); if (digest(context()) !== digest(start))
|
|
794
832
|
throw new Error('assistant-actions: repository readback changed'); };
|
|
795
833
|
const generation = this.#externalObservedGeneration, epochs = this.#externalObservedEpochs, callId = `readback:${randomUUID()}`;
|
|
796
|
-
const response = await this.#externalRequest(undefined, grant, grant.owner, 'inspect', { kind, ...(pullRequestNumber === undefined ? {} : { pullRequestNumber }) }, signal, { callId, rootCallId: start.intent.id }, callId, authorization);
|
|
834
|
+
const response = await this.#externalRequest(undefined, grant, grant.owner, 'inspect', { kind, ...(pullRequestNumber === undefined ? {} : { pullRequestNumber }), ...(commitOid === undefined ? {} : { commitOid }) }, signal, { callId, rootCallId: start.intent.id }, callId, authorization);
|
|
797
835
|
if (response.result.status !== 'succeeded' || response.observed === undefined || generation !== undefined && generation !== response.generation || epochs !== undefined && epochs !== this.#externalObservedEpochs)
|
|
798
836
|
throw new Error('assistant-actions: repository inspection unavailable or broker changed');
|
|
799
837
|
authorization.current();
|
|
800
838
|
return response.observed;
|
|
801
839
|
}
|
|
802
|
-
const request = normalizeWorkflow({ grantId: start.authority.grantId, operation: 'inspect', idempotencyKey: randomUUID(), kind, ...(pullRequestNumber === undefined ? {} : { pullRequestNumber }) });
|
|
840
|
+
const request = normalizeWorkflow({ grantId: start.authority.grantId, operation: 'inspect', idempotencyKey: randomUUID(), kind, ...(pullRequestNumber === undefined ? {} : { pullRequestNumber }), ...(commitOid === undefined ? {} : { commitOid }) });
|
|
803
841
|
if (!('operation' in request))
|
|
804
842
|
throw new Error('assistant-actions: repository inspection invalid');
|
|
805
843
|
const authorization = { sessionId: start.security.sessionId,
|
|
@@ -807,7 +845,7 @@ export class AssistantActionsService extends Service {
|
|
|
807
845
|
throw new Error('assistant-actions: repository readback changed'); return current.security.identity; },
|
|
808
846
|
authorize: record => this.ctx.get('assistantPolicy')?.authorize(this.#deliveryPolicy(start.security), { idempotencyKey: `action:${record.id}` }).effect === 'allow' };
|
|
809
847
|
const result = await this.#runAuthorized(authorization, request, signal, async (actionId, grant, token, combined) => {
|
|
810
|
-
const reply = await this.workflow.inspect({ grant, kind: request.kind, ...(request.pullRequestNumber === undefined ? {} : { pullRequestNumber: request.pullRequestNumber }), token, signal: combined });
|
|
848
|
+
const reply = await this.workflow.inspect({ grant, kind: request.kind, ...(request.pullRequestNumber === undefined ? {} : { pullRequestNumber: request.pullRequestNumber }), ...(request.commitOid === undefined ? {} : { commitOid: request.commitOid }), token, signal: combined });
|
|
811
849
|
observed = reply?.observed;
|
|
812
850
|
return reply ? { actionId, status: 'succeeded' } : { actionId, status: 'failed', reason: 'github-inspect-failed' };
|
|
813
851
|
});
|
|
@@ -971,16 +1009,24 @@ export class AssistantActionsService extends Service {
|
|
|
971
1009
|
this.#deliveryIdentity(intent.security);
|
|
972
1010
|
const grant = this.#externalGrants.get(intent.security.grantId), receipts = outcome.brokerReceipts;
|
|
973
1011
|
const snapshot = this.#inspectDelivery(intent, true);
|
|
974
|
-
if (!receipts?.commit || !
|
|
1012
|
+
if (!receipts?.commit || !snapshot || !outcome.commit.commitOid)
|
|
975
1013
|
throw new Error('assistant-actions: external delivery receipt unavailable');
|
|
976
1014
|
const destination = { classification: 'github-repository', repository: grant.destination.repository, branch: grant.destination.branch,
|
|
977
1015
|
...(grant.destination.baseBranch === undefined ? {} : { baseBranch: grant.destination.baseBranch }) };
|
|
978
1016
|
const common = { grantId: grant.id, grantRevision: grant.revision, grantDigest: grant.grantDigest, owner: grant.owner, sessionId: intent.security.sessionId, source: grant.source, destination };
|
|
979
1017
|
const commit = verifyExternalDeliveryReceipt(receipts.commit, { expectedHeadOid: intent.request.expectedHeadOid, headline: intent.request.headline,
|
|
980
1018
|
files: snapshot.files.map(file => ({ path: file.path, content: file.content })) }, { ...common, operation: 'commit', actionId: this.#externalActionId(grant, intent.security.sessionId, 'commit', `${intent.id}:commit`) }, this.#external());
|
|
1019
|
+
if (commit.result?.operation !== 'commit' || commit.actionId !== outcome.commit.actionId || commit.result.commitOid !== outcome.commit.commitOid)
|
|
1020
|
+
throw new Error('assistant-actions: external delivery receipt changed');
|
|
1021
|
+
if (!intent.request.pullRequest) {
|
|
1022
|
+
if (outcome.pullRequest || receipts.pullRequest)
|
|
1023
|
+
throw new Error('assistant-actions: external delivery receipt changed');
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
if (!receipts.pullRequest || !outcome.pullRequest)
|
|
1027
|
+
throw new Error('assistant-actions: external delivery receipt unavailable');
|
|
981
1028
|
const pr = verifyExternalDeliveryReceipt(receipts.pullRequest, { expectedHeadOid: outcome.commit.commitOid, ...intent.request.pullRequest }, { ...common, operation: 'pull-request', actionId: this.#externalActionId(grant, intent.security.sessionId, 'pull-request', `${intent.id}:pr`) }, this.#external());
|
|
982
|
-
if (
|
|
983
|
-
|| commit.result.commitOid !== outcome.commit.commitOid || pr.result.pullRequestNumber !== outcome.pullRequest.pullRequestNumber)
|
|
1029
|
+
if (pr.result?.operation !== 'pull-request' || pr.actionId !== outcome.pullRequest.actionId || pr.result.pullRequestNumber !== outcome.pullRequest.pullRequestNumber)
|
|
984
1030
|
throw new Error('assistant-actions: external delivery receipt changed');
|
|
985
1031
|
}
|
|
986
1032
|
async #deliverVerified(intent, snapshot, signal) {
|