@atolis-hq/wake 0.4.15 → 0.4.17
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/src/bootstrap/config/root-schema.d.ts +23 -0
- package/dist/src/bootstrap/config/root-schema.js +23 -3
- package/dist/src/bootstrap/index.d.ts +1 -0
- package/dist/src/bootstrap/index.js +1 -0
- package/dist/src/bootstrap/npm-update-port.d.ts +11 -0
- package/dist/src/bootstrap/npm-update-port.js +45 -0
- package/dist/src/bootstrap/self-update-application.js +2 -3
- package/dist/src/bootstrap/surface-api-work-applications.js +13 -2
- package/dist/src/bootstrap/surface-cli-applications.js +50 -18
- package/dist/src/bootstrap/version.js +1 -1
- package/dist/src/integrations/contracts/config.d.ts +13 -0
- package/dist/src/integrations/contracts/config.js +7 -0
- package/dist/src/integrations/contracts/provider.d.ts +4 -1
- package/dist/src/integrations/contracts/provider.js +3 -1
- package/dist/src/integrations/github/application/inbound-comment-syntax.d.ts +1 -1
- package/dist/src/integrations/github/application/inbound-comment-syntax.js +3 -1
- package/dist/src/integrations/github/application/inbound-review-signals.d.ts +2 -0
- package/dist/src/integrations/github/application/inbound-review-signals.js +17 -4
- package/dist/src/integrations/github/application/inbound-translator.d.ts +4 -1
- package/dist/src/integrations/github/application/inbound-translator.js +28 -4
- package/dist/src/integrations/github/contracts/events.d.ts +4 -0
- package/dist/src/integrations/github/contracts/events.js +1 -0
- package/dist/src/integrations/github/infrastructure/comment-source.js +6 -5
- package/dist/src/integrations/github/infrastructure/issue-source.d.ts +1 -1
- package/dist/src/integrations/github/infrastructure/issue-source.js +2 -0
- package/dist/src/integrations/github/provider.js +4 -1
- package/dist/src/orchestration/application/conversation-command.d.ts +22 -0
- package/dist/src/orchestration/application/conversation-command.js +46 -0
- package/dist/src/orchestration/application/orchestration-repository.js +10 -0
- package/dist/src/orchestration/application/orchestration-service.d.ts +10 -1
- package/dist/src/orchestration/application/orchestration-service.js +48 -1
- package/dist/src/orchestration/contracts/config.d.ts +5 -0
- package/dist/src/orchestration/contracts/config.js +6 -1
- package/dist/src/orchestration/contracts/vocabulary.d.ts +15 -0
- package/dist/src/orchestration/contracts/vocabulary.js +13 -0
- package/dist/src/orchestration/index.d.ts +1 -0
- package/dist/src/orchestration/index.js +1 -0
- package/dist/src/surfaces/cli/usage.js +1 -1
- package/package.json +1 -1
|
@@ -140,6 +140,11 @@ declare const hostConfigSchema: z.ZodDefault<z.ZodObject<{
|
|
|
140
140
|
selfUpdate: z.ZodDefault<z.ZodObject<{
|
|
141
141
|
drainTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
142
142
|
cancellationTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
143
|
+
npm: z.ZodDefault<z.ZodObject<{
|
|
144
|
+
package: z.ZodDefault<z.ZodString>;
|
|
145
|
+
distTag: z.ZodDefault<z.ZodString>;
|
|
146
|
+
registry: z.ZodOptional<z.ZodString>;
|
|
147
|
+
}, z.core.$strict>>;
|
|
143
148
|
}, z.core.$strict>>;
|
|
144
149
|
}, z.core.$strict>>;
|
|
145
150
|
export declare const rootConfigSchema: z.ZodObject<{
|
|
@@ -340,13 +345,26 @@ export declare const rootConfigSchema: z.ZodObject<{
|
|
|
340
345
|
integrations: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodPipe<z.ZodObject<{
|
|
341
346
|
provider: z.ZodOptional<z.ZodString>;
|
|
342
347
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
348
|
+
conversation: z.ZodOptional<z.ZodObject<{
|
|
349
|
+
capabilities: z.ZodReadonly<z.ZodArray<z.ZodEnum<{
|
|
350
|
+
"review-surface": "review-surface";
|
|
351
|
+
"chat-surface": "chat-surface";
|
|
352
|
+
"operator-surface": "operator-surface";
|
|
353
|
+
}>>>;
|
|
354
|
+
}, z.core.$strict>>;
|
|
343
355
|
}, z.core.$loose>, z.ZodTransform<{
|
|
344
356
|
enabled: boolean;
|
|
345
357
|
provider?: string | undefined;
|
|
358
|
+
conversation?: {
|
|
359
|
+
capabilities: readonly ("review-surface" | "chat-surface" | "operator-surface")[];
|
|
360
|
+
} | undefined;
|
|
346
361
|
}, {
|
|
347
362
|
[x: string]: unknown;
|
|
348
363
|
enabled: boolean;
|
|
349
364
|
provider?: string | undefined;
|
|
365
|
+
conversation?: {
|
|
366
|
+
capabilities: readonly ("review-surface" | "chat-surface" | "operator-surface")[];
|
|
367
|
+
} | undefined;
|
|
350
368
|
}>>>>;
|
|
351
369
|
surfaces: z.ZodPipe<z.ZodDefault<z.ZodObject<{
|
|
352
370
|
api: z.ZodDefault<z.ZodObject<{
|
|
@@ -423,6 +441,11 @@ export declare const rootConfigSchema: z.ZodObject<{
|
|
|
423
441
|
selfUpdate: z.ZodDefault<z.ZodObject<{
|
|
424
442
|
drainTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
425
443
|
cancellationTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
444
|
+
npm: z.ZodDefault<z.ZodObject<{
|
|
445
|
+
package: z.ZodDefault<z.ZodString>;
|
|
446
|
+
distTag: z.ZodDefault<z.ZodString>;
|
|
447
|
+
registry: z.ZodOptional<z.ZodString>;
|
|
448
|
+
}, z.core.$strict>>;
|
|
426
449
|
}, z.core.$strict>>;
|
|
427
450
|
}, z.core.$strict>>;
|
|
428
451
|
}, z.core.$strict>;
|
|
@@ -35,7 +35,11 @@ const orchestrationConfigSchema = z
|
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
})
|
|
38
|
-
.default({
|
|
38
|
+
.default({
|
|
39
|
+
workflows: {},
|
|
40
|
+
workflowSelectors: [],
|
|
41
|
+
default: 'default',
|
|
42
|
+
});
|
|
39
43
|
const transcriptsConfigSchema = z
|
|
40
44
|
.object({
|
|
41
45
|
enabled: z.boolean().default(false),
|
|
@@ -88,9 +92,21 @@ const hostConfigSchema = z
|
|
|
88
92
|
.object({
|
|
89
93
|
drainTimeoutMs: z.number().int().positive().default(30_000),
|
|
90
94
|
cancellationTimeoutMs: z.number().int().positive().default(30_000),
|
|
95
|
+
npm: z
|
|
96
|
+
.object({
|
|
97
|
+
package: z.string().trim().min(1).default('@atolis-hq/wake'),
|
|
98
|
+
distTag: z.string().trim().min(1).default('latest'),
|
|
99
|
+
registry: z.string().url().optional(),
|
|
100
|
+
})
|
|
101
|
+
.strict()
|
|
102
|
+
.default({ package: '@atolis-hq/wake', distTag: 'latest' }),
|
|
91
103
|
})
|
|
92
104
|
.strict()
|
|
93
|
-
.default({
|
|
105
|
+
.default({
|
|
106
|
+
drainTimeoutMs: 30_000,
|
|
107
|
+
cancellationTimeoutMs: 30_000,
|
|
108
|
+
npm: { package: '@atolis-hq/wake', distTag: 'latest' },
|
|
109
|
+
}),
|
|
94
110
|
})
|
|
95
111
|
.strict()
|
|
96
112
|
.default({
|
|
@@ -104,7 +120,11 @@ const hostConfigSchema = z
|
|
|
104
120
|
extraMounts: [],
|
|
105
121
|
},
|
|
106
122
|
development: {},
|
|
107
|
-
selfUpdate: {
|
|
123
|
+
selfUpdate: {
|
|
124
|
+
drainTimeoutMs: 30_000,
|
|
125
|
+
cancellationTimeoutMs: 30_000,
|
|
126
|
+
npm: { package: '@atolis-hq/wake', distTag: 'latest' },
|
|
127
|
+
},
|
|
108
128
|
})
|
|
109
129
|
.superRefine((value, context) => {
|
|
110
130
|
if (value.development.mode === 'source' && value.development.repoRoot === undefined)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SourceUpdatePort } from './self-update-application.js';
|
|
2
|
+
export interface NpmUpdateProcess {
|
|
3
|
+
execute(command: string, args: readonly string[]): Promise<string>;
|
|
4
|
+
}
|
|
5
|
+
/** Resolves immutable published package versions. Installation is performed by the rollout. */
|
|
6
|
+
export declare function createNpmUpdatePort(input: {
|
|
7
|
+
readonly packageName: string;
|
|
8
|
+
readonly distTag?: string;
|
|
9
|
+
readonly registry?: string;
|
|
10
|
+
readonly execute?: NpmUpdateProcess['execute'];
|
|
11
|
+
}): SourceUpdatePort;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { execFile as nodeExecFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
const execFile = promisify(nodeExecFile);
|
|
4
|
+
/** Resolves immutable published package versions. Installation is performed by the rollout. */
|
|
5
|
+
export function createNpmUpdatePort(input) {
|
|
6
|
+
const execute = input.execute ?? runProcess;
|
|
7
|
+
const distTag = input.distTag ?? 'latest';
|
|
8
|
+
const registry = input.registry === undefined ? [] : [`--registry=${input.registry}`];
|
|
9
|
+
return {
|
|
10
|
+
isClean: async () => true,
|
|
11
|
+
async latestTag() {
|
|
12
|
+
const resolved = await resolveVersion();
|
|
13
|
+
if (resolved === undefined)
|
|
14
|
+
throw new Error(`No npm version is available for ${input.packageName}`);
|
|
15
|
+
return resolved;
|
|
16
|
+
},
|
|
17
|
+
async candidateTags() {
|
|
18
|
+
const resolved = await resolveVersion();
|
|
19
|
+
return resolved === undefined ? [] : [resolved];
|
|
20
|
+
},
|
|
21
|
+
// The Docker rollout installs this exact version. Keeping this side-effect-free means
|
|
22
|
+
// the updater never overwrites the CLI process which is coordinating the update.
|
|
23
|
+
checkout: async () => { },
|
|
24
|
+
healthy: async () => true,
|
|
25
|
+
};
|
|
26
|
+
async function resolveVersion() {
|
|
27
|
+
const output = await execute('npm', [
|
|
28
|
+
'view',
|
|
29
|
+
`${input.packageName}@${distTag}`,
|
|
30
|
+
'version',
|
|
31
|
+
'--json',
|
|
32
|
+
...registry,
|
|
33
|
+
]);
|
|
34
|
+
const parsed = JSON.parse(output);
|
|
35
|
+
const value = Array.isArray(parsed) ? parsed.at(0) : parsed;
|
|
36
|
+
if (typeof value !== 'string' ||
|
|
37
|
+
!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u.test(value))
|
|
38
|
+
throw new Error(`npm returned an invalid version for ${input.packageName}@${distTag}`);
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function runProcess(command, args) {
|
|
43
|
+
const result = await execFile(command, args, { encoding: 'utf8' });
|
|
44
|
+
return result.stdout;
|
|
45
|
+
}
|
|
@@ -10,9 +10,8 @@ export function createSelfUpdateApplication(input) {
|
|
|
10
10
|
async updateLatest(force = false) {
|
|
11
11
|
const candidates = await input.source.candidateTags();
|
|
12
12
|
const [latestCandidate] = candidates;
|
|
13
|
-
if (latestCandidate === undefined)
|
|
14
|
-
throw new Error('No
|
|
15
|
-
}
|
|
13
|
+
if (latestCandidate === undefined)
|
|
14
|
+
throw new Error('No update versions are available');
|
|
16
15
|
for (const tag of candidates) {
|
|
17
16
|
if (!force && (await input.ledger.isBad(tag))) {
|
|
18
17
|
continue;
|
|
@@ -2,7 +2,7 @@ import { correlationId, EventActorKind } from '@atolis-hq/eventing';
|
|
|
2
2
|
import { pullRequestProjection } from '../activities/index.js';
|
|
3
3
|
import { conversationIdForWorkItem, ConversationOriginKind, } from '../conversations/index.js';
|
|
4
4
|
import { executionProjection, runsByWorkflowInstanceProjection, } from '../execution/index.js';
|
|
5
|
-
import { ApprovalAuthorityKind, GroupBudgetExtensionIneligibleError, isGroupBudgetExtensionEligible, OperatorRetryIneligibleError, orchestrationProjection, selectOperatorRetryTarget, workflowsByWorkItemProjection, } from '../orchestration/index.js';
|
|
5
|
+
import { ApprovalAuthorityKind, ConversationSurfaceCapability, GroupBudgetExtensionIneligibleError, isGroupBudgetExtensionEligible, OperatorRetryIneligibleError, orchestrationProjection, selectOperatorRetryTarget, workflowsByWorkItemProjection, } from '../orchestration/index.js';
|
|
6
6
|
import { workCorrelationsProjection, } from '../resources/index.js';
|
|
7
7
|
import { ApiCommandStatus, fromWorkItemKey, presentResource, presentRun, presentWorkflowInstance, presentWorkItem, } from '../surfaces/index.js';
|
|
8
8
|
import { workItemId, WorkStatus } from '../work/index.js';
|
|
@@ -134,7 +134,18 @@ export function createSurfaceWorkApplications(root, now) {
|
|
|
134
134
|
body: command.body,
|
|
135
135
|
origin: { kind: ConversationOriginKind.ControlPlane, actorId: context.actor.id },
|
|
136
136
|
}, context);
|
|
137
|
-
|
|
137
|
+
const dispatch = root.orchestration
|
|
138
|
+
.applyConversationCommand;
|
|
139
|
+
const applied = dispatch === undefined
|
|
140
|
+
? false
|
|
141
|
+
: await dispatch.call(root.orchestration, id, {
|
|
142
|
+
body: command.body,
|
|
143
|
+
actorId: context.actor.id,
|
|
144
|
+
capabilities: [ConversationSurfaceCapability.Operator],
|
|
145
|
+
authorized: true,
|
|
146
|
+
}, context);
|
|
147
|
+
if (!applied)
|
|
148
|
+
await resumeAgentStages(root, id, context);
|
|
138
149
|
return accepted(command.idempotencyKey, now());
|
|
139
150
|
},
|
|
140
151
|
}
|
|
@@ -12,6 +12,7 @@ import { ResourceCorrelationRole, resourceId } from '../resources/index.js';
|
|
|
12
12
|
import { DockerProcessError, createApiDispatcher, createLoggedDockerCli, createPackagedAssetSource, createPairingGrant, createProcessLogSink, createSandboxDockerPort, createSurfaceHttpServer, drainProcessOutput, loadOrCreateCredentials, redeemPairingGrant, replaceAccessKey, runDoctor, runSandbox, runSandboxEntrypoint, runSandboxSetup, runSelfUpdateLatestLoop, runTargetSmoke, verifyResidentStart, waitForActiveRuns, waitForever, } from '../surfaces/index.js';
|
|
13
13
|
import { WorkStreamKind, workItemId } from '../work/index.js';
|
|
14
14
|
import { loadConfig } from './config/load-config.js';
|
|
15
|
+
import { createNpmUpdatePort } from './npm-update-port.js';
|
|
15
16
|
import { runtimeProjectionDefinitions } from './projection-runtime.js';
|
|
16
17
|
import { createRunnerRegistry } from './runner-registry.js';
|
|
17
18
|
import { createOneShotRunnerAdvance, createResidentRunnerAdvance, withFinalProjectionCatchUp, } from './runner-tick-adapter.js';
|
|
@@ -315,7 +316,7 @@ function sandboxDockerOptions(root, overrides) {
|
|
|
315
316
|
wakeRoot: root.paths.wakeRoot,
|
|
316
317
|
containerHomeRoot: root.paths.containerHomeRoot,
|
|
317
318
|
image: overrides?.image ?? root.config.host.sandbox.image,
|
|
318
|
-
development: root.config.host.development,
|
|
319
|
+
development: overrides?.development ?? root.config.host.development,
|
|
319
320
|
containerName: root.config.host.sandbox.containerName,
|
|
320
321
|
...(root.config.surfaces.api.enabled ? { publishedPort: root.config.surfaces.api.port } : {}),
|
|
321
322
|
wakeMountPath: root.config.host.sandbox.wakeMountPath,
|
|
@@ -324,7 +325,9 @@ function sandboxDockerOptions(root, overrides) {
|
|
|
324
325
|
startEnabled: root.config.host.sandbox.start.enabled,
|
|
325
326
|
...(process.env.WAKE_MEMORY_PROFILE === 'runner' ? { memoryProfile: 'runner' } : {}),
|
|
326
327
|
inspect: createDockerInspection(root.paths.wakeRoot),
|
|
327
|
-
resolveBuildVersion: () =>
|
|
328
|
+
resolveBuildVersion: async () => overrides?.buildVersion === undefined
|
|
329
|
+
? resolveSandboxBuildVersion(root)
|
|
330
|
+
: overrides.buildVersion,
|
|
328
331
|
};
|
|
329
332
|
}
|
|
330
333
|
function createSandboxDocker(root, overrides) {
|
|
@@ -408,11 +411,13 @@ function createSandboxRuntimeApplications(root) {
|
|
|
408
411
|
* resident `wake start` process actually came back up. Reused for both the
|
|
409
412
|
* forward deploy and a same-shaped rollback to a previously-built tag.
|
|
410
413
|
*/
|
|
411
|
-
async function deploySandboxTag(root, docker, tag, image) {
|
|
412
|
-
const port = createSandboxDockerPort(docker, sandboxDockerOptions(root, { image }));
|
|
414
|
+
async function deploySandboxTag(root, docker, tag, image, overrides) {
|
|
415
|
+
const port = createSandboxDockerPort(docker, sandboxDockerOptions(root, { image, ...overrides }));
|
|
413
416
|
await port.build();
|
|
414
417
|
await port.update();
|
|
415
|
-
const wakeInvocation =
|
|
418
|
+
const wakeInvocation = overrides?.development?.mode === 'source'
|
|
419
|
+
? ['node', '/app/dist/src/main.js']
|
|
420
|
+
: sandboxWakeInvocation(root);
|
|
416
421
|
await verifyResidentStart(docker, root.config.host.sandbox.containerName, [...wakeInvocation, 'start', '--wake-root', '/wake'].join(' '));
|
|
417
422
|
await port.exec([
|
|
418
423
|
...wakeInvocation,
|
|
@@ -421,10 +426,12 @@ async function deploySandboxTag(root, docker, tag, image) {
|
|
|
421
426
|
`/tmp/wake-self-update-healthcheck-${tag}`,
|
|
422
427
|
]);
|
|
423
428
|
}
|
|
424
|
-
async function rollbackSandboxTag(root, docker, image) {
|
|
429
|
+
async function rollbackSandboxTag(root, docker, image, development) {
|
|
425
430
|
const port = createSandboxDockerPort(docker, sandboxDockerOptions(root, { image }));
|
|
426
431
|
await port.update();
|
|
427
|
-
const wakeInvocation =
|
|
432
|
+
const wakeInvocation = development?.mode === 'source'
|
|
433
|
+
? ['node', '/app/dist/src/main.js']
|
|
434
|
+
: sandboxWakeInvocation(root);
|
|
428
435
|
await verifyResidentStart(docker, root.config.host.sandbox.containerName, [...wakeInvocation, 'start', '--wake-root', '/wake'].join(' '));
|
|
429
436
|
}
|
|
430
437
|
function createOperationalApplications(root) {
|
|
@@ -450,31 +457,56 @@ function createOperationalApplications(root) {
|
|
|
450
457
|
sandboxEntrypoint: async () => {
|
|
451
458
|
await runSandboxEntrypoint(createSandboxEntrypointDependencies(root));
|
|
452
459
|
},
|
|
460
|
+
// eslint-disable-next-line complexity -- update source and loop options are validated at this CLI boundary.
|
|
453
461
|
selfUpdate: async (arguments_) => {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const repoRoot = root.config.host.development.repoRoot;
|
|
457
|
-
if (
|
|
462
|
+
const sourceRoot = optionalOperationalOption(arguments_, '--source');
|
|
463
|
+
const configuredSource = root.config.host.development.mode === 'source';
|
|
464
|
+
const repoRoot = sourceRoot ?? root.config.host.development.repoRoot;
|
|
465
|
+
if (sourceRoot !== undefined && root.config.host.development.mode === 'source')
|
|
466
|
+
throw new Error('wake self-update --source cannot override source-mode configuration');
|
|
467
|
+
if (configuredSource && repoRoot === undefined)
|
|
458
468
|
throw new Error('wake self-update requires host.development.repoRoot');
|
|
459
469
|
const tag = optionalOperationalOption(arguments_, '--tag');
|
|
470
|
+
const version = optionalOperationalOption(arguments_, '--version');
|
|
471
|
+
if (tag !== undefined && version !== undefined)
|
|
472
|
+
throw new Error('wake self-update accepts only one of --tag or --version');
|
|
473
|
+
if (version !== undefined && (configuredSource || sourceRoot !== undefined))
|
|
474
|
+
throw new Error('wake self-update --version is only valid for npm updates');
|
|
475
|
+
const useSource = configuredSource || sourceRoot !== undefined;
|
|
476
|
+
const updatePort = useSource
|
|
477
|
+
? createSourceUpdatePort({ repoRoot: repoRoot })
|
|
478
|
+
: createNpmUpdatePort({
|
|
479
|
+
packageName: root.config.host.selfUpdate.npm.package,
|
|
480
|
+
distTag: root.config.host.selfUpdate.npm.distTag,
|
|
481
|
+
...(root.config.host.selfUpdate.npm.registry === undefined
|
|
482
|
+
? {}
|
|
483
|
+
: { registry: root.config.host.selfUpdate.npm.registry }),
|
|
484
|
+
});
|
|
485
|
+
const updateTag = tag ?? version;
|
|
486
|
+
const updateDevelopment = useSource
|
|
487
|
+
? { mode: 'source', repoRoot: repoRoot }
|
|
488
|
+
: { mode: 'packaged' };
|
|
460
489
|
const dockerCli = createDockerCliForRoot(root);
|
|
461
490
|
const imageRepository = root.config.host.sandbox.imageRepository;
|
|
462
491
|
const failureLog = createSelfUpdateFailureLog(root.paths.wakeRoot);
|
|
463
492
|
const application = createSelfUpdateApplication({
|
|
464
493
|
ledger: createUpdateLedger(root.paths.wakeRoot),
|
|
465
|
-
source:
|
|
494
|
+
source: updatePort,
|
|
466
495
|
rollout: {
|
|
467
496
|
async deploy(deployTag) {
|
|
468
497
|
await waitForActiveRuns({
|
|
469
498
|
activeRunIds: () => activeExecutionRunIds(root),
|
|
470
499
|
sleep: async (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)),
|
|
471
500
|
});
|
|
472
|
-
await deploySandboxTag(root, dockerCli, deployTag, `${imageRepository}:${deployTag}
|
|
501
|
+
await deploySandboxTag(root, dockerCli, deployTag, `${imageRepository}:${deployTag}`, {
|
|
502
|
+
development: updateDevelopment,
|
|
503
|
+
buildVersion: deployTag,
|
|
504
|
+
});
|
|
473
505
|
// A later deploy succeeding after a prior rollback clears the stale warning.
|
|
474
506
|
await failureLog.clear();
|
|
475
507
|
},
|
|
476
508
|
async rollback(rollbackTag) {
|
|
477
|
-
await rollbackSandboxTag(root, dockerCli, `${imageRepository}:${rollbackTag}
|
|
509
|
+
await rollbackSandboxTag(root, dockerCli, `${imageRepository}:${rollbackTag}`, updateDevelopment);
|
|
478
510
|
},
|
|
479
511
|
recordFailure: (failedTag, error) => failureLog.record(failedTag, error),
|
|
480
512
|
},
|
|
@@ -485,12 +517,12 @@ function createOperationalApplications(root) {
|
|
|
485
517
|
const force = arguments_.includes('--force');
|
|
486
518
|
if (arguments_.includes('--loop')) {
|
|
487
519
|
const intervalMs = selfUpdateLoopInterval(arguments_);
|
|
488
|
-
if (
|
|
489
|
-
return runSelfUpdateLatestLoop(async () => application.update(
|
|
520
|
+
if (updateTag !== undefined)
|
|
521
|
+
return runSelfUpdateLatestLoop(async () => application.update(updateTag, force), () => delay(intervalMs));
|
|
490
522
|
return runSelfUpdateLatestLoop(async () => application.updateLatest(force), () => delay(intervalMs));
|
|
491
523
|
}
|
|
492
|
-
if (
|
|
493
|
-
return { tag, updated: await application.update(
|
|
524
|
+
if (updateTag !== undefined)
|
|
525
|
+
return { tag: updateTag, updated: await application.update(updateTag, force) };
|
|
494
526
|
return application.updateLatest(force);
|
|
495
527
|
},
|
|
496
528
|
smoke: async () => runTargetSmoke(createRunnerRegistry(root.config.execution, root.fakeScenarios), root.config.execution.defaultRunnerPool, new AbortController().signal),
|
|
@@ -124,6 +124,6 @@ export function resolveWakeVersion(options = {}) {
|
|
|
124
124
|
return `g${headHash.slice(0, 7)}`;
|
|
125
125
|
return '0.1.0-dev';
|
|
126
126
|
}
|
|
127
|
-
export const wakeVersion = "v0.4.
|
|
127
|
+
export const wakeVersion = "v0.4.17";
|
|
128
128
|
/** The registry-installable version used by packaged sandbox images. */
|
|
129
129
|
export const wakePackageVersion = packageVersion(defaultRepoRoot) ?? wakeVersion;
|
|
@@ -2,12 +2,25 @@ import { z } from 'zod';
|
|
|
2
2
|
export declare const integrationsConfigSchema: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodPipe<z.ZodObject<{
|
|
3
3
|
provider: z.ZodOptional<z.ZodString>;
|
|
4
4
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
5
|
+
conversation: z.ZodOptional<z.ZodObject<{
|
|
6
|
+
capabilities: z.ZodReadonly<z.ZodArray<z.ZodEnum<{
|
|
7
|
+
"review-surface": "review-surface";
|
|
8
|
+
"chat-surface": "chat-surface";
|
|
9
|
+
"operator-surface": "operator-surface";
|
|
10
|
+
}>>>;
|
|
11
|
+
}, z.core.$strict>>;
|
|
5
12
|
}, z.core.$loose>, z.ZodTransform<{
|
|
6
13
|
enabled: boolean;
|
|
7
14
|
provider?: string | undefined;
|
|
15
|
+
conversation?: {
|
|
16
|
+
capabilities: readonly ("review-surface" | "chat-surface" | "operator-surface")[];
|
|
17
|
+
} | undefined;
|
|
8
18
|
}, {
|
|
9
19
|
[x: string]: unknown;
|
|
10
20
|
enabled: boolean;
|
|
11
21
|
provider?: string | undefined;
|
|
22
|
+
conversation?: {
|
|
23
|
+
capabilities: readonly ("review-surface" | "chat-surface" | "operator-surface")[];
|
|
24
|
+
} | undefined;
|
|
12
25
|
}>>>>;
|
|
13
26
|
export type IntegrationsConfig = z.infer<typeof integrationsConfigSchema>;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { surfaceCapabilitySchema } from '../../orchestration/index.js';
|
|
3
|
+
const conversationConfigSchema = z
|
|
4
|
+
.object({ capabilities: z.array(surfaceCapabilitySchema).readonly() })
|
|
5
|
+
.strict();
|
|
2
6
|
const providerEntrySchema = z
|
|
3
7
|
.looseObject({
|
|
4
8
|
provider: z
|
|
@@ -7,6 +11,9 @@ const providerEntrySchema = z
|
|
|
7
11
|
.regex(/^[a-z][a-z0-9-]*$/)
|
|
8
12
|
.optional(),
|
|
9
13
|
enabled: z.boolean().default(true),
|
|
14
|
+
// This is adapter-instance policy. It is intentionally outside provider-specific
|
|
15
|
+
// configuration so two instances of one provider can have different authority.
|
|
16
|
+
conversation: conversationConfigSchema.optional(),
|
|
10
17
|
})
|
|
11
18
|
.transform((value) => ({ ...value, enabled: value.enabled ?? true }));
|
|
12
19
|
export const integrationsConfigSchema = z
|
|
@@ -3,7 +3,7 @@ import type { PullRequestService } from '../../activities/index.js';
|
|
|
3
3
|
import type { ConversationService } from '../../conversations/index.js';
|
|
4
4
|
import type { RunRepository } from '../../execution/index.js';
|
|
5
5
|
import type { Clock, IdGenerator } from '../../kernel/index.js';
|
|
6
|
-
import type { OrchestrationService, WorkflowCandidate, WorkflowName } from '../../orchestration/index.js';
|
|
6
|
+
import type { OrchestrationService, SurfaceCapability, WorkflowCandidate, WorkflowName } from '../../orchestration/index.js';
|
|
7
7
|
import type { ExternalResourceKey, ResourceCapability, ResourceKind, ResourceLookup, ResourceService } from '../../resources/index.js';
|
|
8
8
|
import type { WorkItemId, WorkItemView, WorkService } from '../../work/index.js';
|
|
9
9
|
import type { ExternalDeliveryAdapter } from '../delivery/contracts/config.js';
|
|
@@ -78,6 +78,8 @@ export type ComposedProviderInstance = Omit<ProviderInstance, 'provider'>;
|
|
|
78
78
|
export interface ProviderDefinition<Config = unknown> {
|
|
79
79
|
readonly provider: string;
|
|
80
80
|
readonly eventTypes: readonly string[];
|
|
81
|
+
/** Conservative default authority for each instance of this provider type. */
|
|
82
|
+
readonly defaultConversationCapabilities?: readonly SurfaceCapability[];
|
|
81
83
|
/** Provider-owned periodic reconciliation, invoked in the tick react phase. */
|
|
82
84
|
readonly maintenance?: {
|
|
83
85
|
readonly runOnce: () => Promise<void>;
|
|
@@ -86,6 +88,7 @@ export interface ProviderDefinition<Config = unknown> {
|
|
|
86
88
|
create(input: {
|
|
87
89
|
readonly adapter: AdapterId;
|
|
88
90
|
readonly config: Config;
|
|
91
|
+
readonly conversationCapabilities?: readonly SurfaceCapability[];
|
|
89
92
|
readonly services?: ProviderServices;
|
|
90
93
|
}): ComposedProviderInstance;
|
|
91
94
|
}
|
|
@@ -18,10 +18,12 @@ export class ProviderRegistry {
|
|
|
18
18
|
throw new Error(`Provider ${provider} is not registered`);
|
|
19
19
|
const adapter = adapterId(name);
|
|
20
20
|
try {
|
|
21
|
+
const { conversation: _conversation, provider: _provider, ...providerConfig } = entry;
|
|
21
22
|
instances.push({
|
|
22
23
|
...definition.create({
|
|
23
24
|
adapter,
|
|
24
|
-
config: definition.parseConfig(
|
|
25
|
+
config: definition.parseConfig(providerConfig),
|
|
26
|
+
conversationCapabilities: entry.conversation?.capabilities ?? definition.defaultConversationCapabilities ?? [],
|
|
25
27
|
...(services === undefined ? {} : { services }),
|
|
26
28
|
}),
|
|
27
29
|
provider,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ReviewActorKind } from '../../../activities/index.js';
|
|
2
2
|
import { GitHubBuiltInCommand } from '../contracts/vocabulary.js';
|
|
3
3
|
export declare function isHumanNonWakeReply(actorKind: ReviewActorKind, body: string): boolean;
|
|
4
|
-
type IssueCommand = typeof GitHubBuiltInCommand.Approved | typeof GitHubBuiltInCommand.Changes | typeof GitHubBuiltInCommand.Retry | typeof GitHubBuiltInCommand.Restart | typeof GitHubBuiltInCommand.Extend;
|
|
4
|
+
type IssueCommand = typeof GitHubBuiltInCommand.Approved | typeof GitHubBuiltInCommand.Accepted | typeof GitHubBuiltInCommand.Changes | typeof GitHubBuiltInCommand.Retry | typeof GitHubBuiltInCommand.Restart | typeof GitHubBuiltInCommand.Extend;
|
|
5
5
|
export declare function recognizedCommand(body: string): IssueCommand | null;
|
|
6
6
|
export declare function isPlainReply(body: string): boolean;
|
|
7
7
|
export declare function shouldResumeBlockedStage(command: IssueCommand | null, plainReply: boolean): boolean;
|
|
@@ -16,13 +16,15 @@ export function recognizedCommand(body) {
|
|
|
16
16
|
const line = commandLine(body);
|
|
17
17
|
if (matchesCommand(line, GitHubBuiltInCommand.Approved))
|
|
18
18
|
return GitHubBuiltInCommand.Approved;
|
|
19
|
+
if (matchesCommand(line, GitHubBuiltInCommand.Accepted))
|
|
20
|
+
return GitHubBuiltInCommand.Accepted;
|
|
19
21
|
if (matchesCommand(line, GitHubBuiltInCommand.Changes))
|
|
20
22
|
return GitHubBuiltInCommand.Changes;
|
|
21
23
|
if (matchesCommand(line, GitHubBuiltInCommand.Retry))
|
|
22
24
|
return GitHubBuiltInCommand.Retry;
|
|
23
25
|
if (matchesCommand(line, GitHubBuiltInCommand.Restart))
|
|
24
26
|
return GitHubBuiltInCommand.Restart;
|
|
25
|
-
if (line
|
|
27
|
+
if (matchesCommand(line, GitHubBuiltInCommand.Extend))
|
|
26
28
|
return GitHubBuiltInCommand.Extend;
|
|
27
29
|
return null;
|
|
28
30
|
}
|
|
@@ -17,5 +17,7 @@ export declare function applyReviewSignal(input: {
|
|
|
17
17
|
readonly ids: IdGenerator;
|
|
18
18
|
readonly adapter: AdapterId;
|
|
19
19
|
readonly orchestration: OrchestrationService | undefined;
|
|
20
|
+
/** The shared conversation dispatcher owns slash commands in the normal inbound path. */
|
|
21
|
+
readonly applyIssueCommands?: boolean;
|
|
20
22
|
}): Promise<void>;
|
|
21
23
|
export {};
|
|
@@ -8,12 +8,20 @@ import { commandContext } from './inbound-context.js';
|
|
|
8
8
|
import { ignoreIneligibleOperatorRetry } from './operator-retry-command.js';
|
|
9
9
|
import { translateGitHubReviewCommand } from './review-command-translator.js';
|
|
10
10
|
export async function applyReviewSignal(input) {
|
|
11
|
-
const { event, journal, resources, work, lookup, adapter, orchestration } = input;
|
|
11
|
+
const { event, journal, resources, work, lookup, adapter, orchestration, applyIssueCommands } = input;
|
|
12
12
|
if (journal === undefined || resources === undefined || work === undefined)
|
|
13
13
|
return;
|
|
14
14
|
const payload = event.event.payload;
|
|
15
15
|
if (payload.reviewKind === 'issue') {
|
|
16
|
-
await applyIssueReviewSignal({
|
|
16
|
+
await applyIssueReviewSignal({
|
|
17
|
+
event,
|
|
18
|
+
resources,
|
|
19
|
+
work,
|
|
20
|
+
lookup,
|
|
21
|
+
orchestration,
|
|
22
|
+
adapter,
|
|
23
|
+
...(applyIssueCommands === undefined ? {} : { applyIssueCommands }),
|
|
24
|
+
});
|
|
17
25
|
return;
|
|
18
26
|
}
|
|
19
27
|
await applyFormalReviewSignal({ ...input, journal, resources, work });
|
|
@@ -62,7 +70,7 @@ async function applyFormalReviewSignal(input) {
|
|
|
62
70
|
});
|
|
63
71
|
}
|
|
64
72
|
async function applyIssueReviewSignal(input) {
|
|
65
|
-
const { event, resources, work, lookup, orchestration, adapter } = input;
|
|
73
|
+
const { event, resources, work, lookup, orchestration, adapter, applyIssueCommands } = input;
|
|
66
74
|
if (!isHumanNonWakeReply(event.event.payload.actor.kind, event.event.payload.body))
|
|
67
75
|
return;
|
|
68
76
|
if (resources === undefined || lookup === undefined || orchestration === undefined)
|
|
@@ -76,7 +84,11 @@ async function applyIssueReviewSignal(input) {
|
|
|
76
84
|
const resource = await resources.get(resourceIdValue);
|
|
77
85
|
const command = recognizedCommand(event.event.payload.body);
|
|
78
86
|
const plainReply = isPlainReply(event.event.payload.body);
|
|
79
|
-
|
|
87
|
+
// Direct compatibility callers retain the legacy handler; the normal inbound
|
|
88
|
+
// path records first and has the shared dispatcher handle the command exactly once.
|
|
89
|
+
if (command !== null) {
|
|
90
|
+
if (applyIssueCommands === false)
|
|
91
|
+
return;
|
|
80
92
|
return applyIssueCommand({
|
|
81
93
|
event,
|
|
82
94
|
command,
|
|
@@ -88,6 +100,7 @@ async function applyIssueReviewSignal(input) {
|
|
|
88
100
|
adapter,
|
|
89
101
|
resourceId: resourceIdValue,
|
|
90
102
|
});
|
|
103
|
+
}
|
|
91
104
|
if (resource?.kind === BuiltInResourceKind.PullRequest) {
|
|
92
105
|
await applyWorkflowSignal({
|
|
93
106
|
event,
|
|
@@ -3,7 +3,7 @@ import { type ObservePullRequest, type PullRequestService } from '../../../activ
|
|
|
3
3
|
import { type ConversationService } from '../../../conversations/index.js';
|
|
4
4
|
import type { RunRepository } from '../../../execution/index.js';
|
|
5
5
|
import { type IdGenerator } from '../../../kernel/index.js';
|
|
6
|
-
import type { OrchestrationService } from '../../../orchestration/index.js';
|
|
6
|
+
import type { ConversationSurfaceCapability, OrchestrationService } from '../../../orchestration/index.js';
|
|
7
7
|
import type { ResourceLookup, ResourceService } from '../../../resources/index.js';
|
|
8
8
|
import { type ResourceId } from '../../../resources/index.js';
|
|
9
9
|
import type { WorkService } from '../../../work/index.js';
|
|
@@ -44,6 +44,7 @@ interface InboundTranslatorDependencies {
|
|
|
44
44
|
readonly intake?: readonly GitHubIntakeRuleConfig[];
|
|
45
45
|
readonly conclusion?: WorkConclusion;
|
|
46
46
|
readonly conversations?: ConversationService;
|
|
47
|
+
readonly conversationCapabilities?: readonly ConversationSurfaceCapability[];
|
|
47
48
|
}
|
|
48
49
|
export declare class InboundTranslator {
|
|
49
50
|
private readonly journal?;
|
|
@@ -64,6 +65,7 @@ export declare class InboundTranslator {
|
|
|
64
65
|
private readonly intake;
|
|
65
66
|
private readonly conclusion;
|
|
66
67
|
private readonly conversations;
|
|
68
|
+
private readonly conversationCapabilities;
|
|
67
69
|
private conversationRecordRecoveryPending;
|
|
68
70
|
private reconcileOnce;
|
|
69
71
|
private selectInboundEvent;
|
|
@@ -73,6 +75,7 @@ export declare class InboundTranslator {
|
|
|
73
75
|
private recordConversationDeferred;
|
|
74
76
|
private appendConversationRecordFact;
|
|
75
77
|
private recordConversationEntry;
|
|
78
|
+
private applyConversationCommand;
|
|
76
79
|
private failureRecorded;
|
|
77
80
|
private retryRecorded;
|
|
78
81
|
private recordTranslationRecovery;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable max-lines */
|
|
2
2
|
import { correlationId, defineEventProcessor, EventActorKind, EventProcessorCategory, EventProcessorReplayPolicy, EventSourceKind, WrongExpectedSequenceError, } from '@atolis-hq/eventing';
|
|
3
|
-
import { ActivityEventType, createPullRequestService, selectActivityEvent, } from '../../../activities/index.js';
|
|
3
|
+
import { ActivityEventType, createPullRequestService, isReviewAuthorized, ReviewerAuthorizationSource, selectActivityEvent, } from '../../../activities/index.js';
|
|
4
4
|
import { conversationIdForWorkItem, ConversationOriginKind, } from '../../../conversations/index.js';
|
|
5
5
|
import { UlidIdGenerator } from '../../../kernel/index.js';
|
|
6
6
|
import { BuiltInResourceCapability, BuiltInResourceKind, ResourceCorrelationRole, ResourceEventType, resourceId, resourceStream, selectResourceEvent, } from '../../../resources/index.js';
|
|
@@ -12,7 +12,7 @@ import { deliveryStream, integrationStream } from '../../contracts/streams.js';
|
|
|
12
12
|
import { DeliveryEventType, selectDeliveryEvent } from '../../delivery/contracts/events.js';
|
|
13
13
|
import { createGitHubEventData } from '../contracts/event-factory.js';
|
|
14
14
|
import { GitHubEventType, selectGitHubAdapterEvent } from '../contracts/events.js';
|
|
15
|
-
import { GitHubAdapter } from '../contracts/vocabulary.js';
|
|
15
|
+
import { GitHubAdapter, UnknownGitHubIdentity } from '../contracts/vocabulary.js';
|
|
16
16
|
import { commandContext } from './inbound-context.js';
|
|
17
17
|
import { applyReviewSignal } from './inbound-review-signals.js';
|
|
18
18
|
import { applyWatchGateVerdictSignal } from './inbound-watch-gate-signals.js';
|
|
@@ -62,6 +62,7 @@ export class InboundTranslator {
|
|
|
62
62
|
this.intake = gitHubIntakeRules(dependencies.intake ?? []);
|
|
63
63
|
this.conclusion = dependencies.conclusion;
|
|
64
64
|
this.conversations = dependencies.conversations;
|
|
65
|
+
this.conversationCapabilities = dependencies.conversationCapabilities ?? [];
|
|
65
66
|
this.processor = defineEventProcessor({
|
|
66
67
|
consumer: `reactor:integration.${this.adapter}.inbound`,
|
|
67
68
|
name: `integration.${this.adapter}.inbound`,
|
|
@@ -83,6 +84,7 @@ export class InboundTranslator {
|
|
|
83
84
|
intake;
|
|
84
85
|
conclusion;
|
|
85
86
|
conversations;
|
|
87
|
+
conversationCapabilities;
|
|
86
88
|
conversationRecordRecoveryPending;
|
|
87
89
|
async reconcileOnce() {
|
|
88
90
|
if (this.journal === undefined || this.work === undefined || this.resources === undefined) {
|
|
@@ -119,6 +121,7 @@ export class InboundTranslator {
|
|
|
119
121
|
ids: this.ids,
|
|
120
122
|
adapter: this.adapter,
|
|
121
123
|
orchestration: this.orchestration,
|
|
124
|
+
applyIssueCommands: false,
|
|
122
125
|
});
|
|
123
126
|
await applyWatchGateVerdictSignal({
|
|
124
127
|
event: owned,
|
|
@@ -223,7 +226,7 @@ export class InboundTranslator {
|
|
|
223
226
|
entry.origin.messageId === externalId);
|
|
224
227
|
if (existing?.entries.some((entry) => entry.representations.some((representation) => representation.resourceId === resource.resourceId &&
|
|
225
228
|
representation.externalId === externalId)))
|
|
226
|
-
return;
|
|
229
|
+
return this.applyConversationCommand(correlation.workItemId, event);
|
|
227
230
|
if (priorEntry !== undefined) {
|
|
228
231
|
if (priorEntry.body !== event.event.payload.body)
|
|
229
232
|
await this.conversations.revise({
|
|
@@ -231,7 +234,7 @@ export class InboundTranslator {
|
|
|
231
234
|
entryId: priorEntry.entryId,
|
|
232
235
|
body: event.event.payload.body,
|
|
233
236
|
}, commandContext(event));
|
|
234
|
-
return;
|
|
237
|
+
return this.applyConversationCommand(correlation.workItemId, event);
|
|
235
238
|
}
|
|
236
239
|
await this.conversations.record({
|
|
237
240
|
conversationId,
|
|
@@ -250,6 +253,18 @@ export class InboundTranslator {
|
|
|
250
253
|
: { location: event.event.payload.location }),
|
|
251
254
|
},
|
|
252
255
|
}, commandContext(event));
|
|
256
|
+
await this.applyConversationCommand(correlation.workItemId, event);
|
|
257
|
+
}
|
|
258
|
+
async applyConversationCommand(workItemId, event) {
|
|
259
|
+
// Native formal reviews already carry their own verified decision and workflow signal.
|
|
260
|
+
if (event.event.payload.reviewKind !== 'issue')
|
|
261
|
+
return;
|
|
262
|
+
await this.orchestration?.applyConversationCommand(workItemId, {
|
|
263
|
+
body: event.event.payload.body,
|
|
264
|
+
actorId: event.event.payload.actor.id,
|
|
265
|
+
capabilities: this.conversationCapabilities,
|
|
266
|
+
authorized: isAuthorizedConversationActor(event),
|
|
267
|
+
}, commandContext(event));
|
|
253
268
|
}
|
|
254
269
|
async failureRecorded(sourceEventId) {
|
|
255
270
|
return (await this.journal.readStream(integrationStream(this.adapter))).some((event) => {
|
|
@@ -708,6 +723,15 @@ export class InboundTranslator {
|
|
|
708
723
|
}
|
|
709
724
|
}
|
|
710
725
|
}
|
|
726
|
+
function isAuthorizedConversationActor(event) {
|
|
727
|
+
const { actor, authorization, resourceAuthorId } = event.event.payload;
|
|
728
|
+
return isReviewAuthorized({
|
|
729
|
+
actorId: actor.id,
|
|
730
|
+
actorKind: actor.kind,
|
|
731
|
+
resourceAuthorId: resourceAuthorId ?? UnknownGitHubIdentity,
|
|
732
|
+
authorization: authorization ?? { source: ReviewerAuthorizationSource.None },
|
|
733
|
+
});
|
|
734
|
+
}
|
|
711
735
|
function observedCommentExternalId(event) {
|
|
712
736
|
const id = event.event.payload.raw.id;
|
|
713
737
|
return typeof id === 'string' || typeof id === 'number' ? String(id) : event.event.eventId;
|
|
@@ -31,6 +31,8 @@ export interface ExternalWorkObservedPayload {
|
|
|
31
31
|
readonly id: string;
|
|
32
32
|
readonly kind: typeof ReviewActorKind.Human | typeof ReviewActorKind.Bot;
|
|
33
33
|
};
|
|
34
|
+
/** Issue or pull-request author, used to reject self-approval. */
|
|
35
|
+
readonly resourceAuthorId?: string | undefined;
|
|
34
36
|
readonly labels?: readonly string[] | undefined;
|
|
35
37
|
readonly assignees?: readonly string[] | undefined;
|
|
36
38
|
readonly changedFiles?: readonly string[] | undefined;
|
|
@@ -71,6 +73,8 @@ interface GitHubIssueCommentObservedPayload {
|
|
|
71
73
|
readonly id: string;
|
|
72
74
|
readonly kind: typeof ReviewActorKind.Human | typeof ReviewActorKind.Bot;
|
|
73
75
|
};
|
|
76
|
+
/** Issue or pull-request author, used to reject self-approval. */
|
|
77
|
+
readonly resourceAuthorId?: string | undefined;
|
|
74
78
|
/** Provider-derived authority for an operator command, when available. */
|
|
75
79
|
readonly authorization?: ReviewerAuthorizationEvidence | undefined;
|
|
76
80
|
readonly raw: Readonly<Record<string, unknown>>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReviewerAuthorizationSource, } from '../../../activities/index.js';
|
|
2
|
-
import {
|
|
2
|
+
import { conversationCommand } from '../../../orchestration/index.js';
|
|
3
3
|
import { issueCommentObservation } from './issue-source.js';
|
|
4
4
|
import { mergeBatches, reportPartialPollFailure } from './poll-watermark.js';
|
|
5
5
|
import { githubReviewObservation } from './review-source.js';
|
|
@@ -59,7 +59,7 @@ export async function issueCommentEventsFor(context, issues, since) {
|
|
|
59
59
|
return mergeBatches(items);
|
|
60
60
|
}
|
|
61
61
|
async function issueCommentEventsForComment(context, issue, comment) {
|
|
62
|
-
const authorization = await
|
|
62
|
+
const authorization = await commandAuthorization(context, comment);
|
|
63
63
|
const event = issueCommentObservation({
|
|
64
64
|
repository: context.repository,
|
|
65
65
|
issue,
|
|
@@ -69,9 +69,10 @@ async function issueCommentEventsForComment(context, issue, comment) {
|
|
|
69
69
|
});
|
|
70
70
|
return event === null ? [] : [event];
|
|
71
71
|
}
|
|
72
|
-
async function
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
async function commandAuthorization(context, comment) {
|
|
73
|
+
if (comment.body === null ||
|
|
74
|
+
comment.body === undefined ||
|
|
75
|
+
conversationCommand(comment.body) === null)
|
|
75
76
|
return undefined;
|
|
76
77
|
const login = comment.user?.login;
|
|
77
78
|
if (login === undefined || context.client.collaboratorPermission === undefined)
|
|
@@ -11,7 +11,7 @@ export declare function issueObservation(input: {
|
|
|
11
11
|
}>;
|
|
12
12
|
export declare function issueCommentObservation(input: {
|
|
13
13
|
readonly repository: string;
|
|
14
|
-
readonly issue: Pick<GitHubIssuePayload, 'number'>;
|
|
14
|
+
readonly issue: Pick<GitHubIssuePayload, 'number' | 'user'>;
|
|
15
15
|
readonly comment: GitHubIssueCommentPayload;
|
|
16
16
|
readonly authorization?: ReviewerAuthorizationEvidence;
|
|
17
17
|
readonly adapter?: AdapterId;
|
|
@@ -69,6 +69,7 @@ function parseRepository(repository) {
|
|
|
69
69
|
throw new Error(`Invalid GitHub repository: ${repository}`);
|
|
70
70
|
return { owner, repo };
|
|
71
71
|
}
|
|
72
|
+
// eslint-disable-next-line complexity -- provider payload normalization keeps one canonical envelope.
|
|
72
73
|
export function issueCommentObservation(input) {
|
|
73
74
|
const body = input.comment.body?.trim();
|
|
74
75
|
if (body === undefined || body.length === 0)
|
|
@@ -96,6 +97,7 @@ export function issueCommentObservation(input) {
|
|
|
96
97
|
id: input.comment.user?.login ?? UnknownGitHubIdentity,
|
|
97
98
|
kind: input.comment.user?.type === 'Bot' ? ReviewActorKind.Bot : ReviewActorKind.Human,
|
|
98
99
|
},
|
|
100
|
+
resourceAuthorId: input.issue.user?.login ?? UnknownGitHubIdentity,
|
|
99
101
|
...(input.authorization === undefined ? {} : { authorization: input.authorization }),
|
|
100
102
|
raw: { id: input.comment.id },
|
|
101
103
|
},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BuiltInActivityName, PullRequestState } from '../../activities/index.js';
|
|
2
|
+
import { ConversationSurfaceCapability } from '../../orchestration/index.js';
|
|
2
3
|
import { BuiltInResourceCapability, BuiltInResourceKind, ResourceCorrelationRole, resourceId, resourceKind, } from '../../resources/index.js';
|
|
3
4
|
import { ArtifactVerificationResult } from '../contracts/artifact-vocabulary.js';
|
|
4
5
|
import { InboundTranslator } from './application/inbound-translator.js';
|
|
@@ -16,10 +17,11 @@ import { createGitHubSource } from './infrastructure/source.js';
|
|
|
16
17
|
export const gitHubProviderDefinition = {
|
|
17
18
|
provider: 'github',
|
|
18
19
|
eventTypes: Object.values(GitHubEventType),
|
|
20
|
+
defaultConversationCapabilities: [ConversationSurfaceCapability.Review],
|
|
19
21
|
parseConfig(value) {
|
|
20
22
|
return gitHubConfigSchema.parse(value);
|
|
21
23
|
},
|
|
22
|
-
create({ adapter, config, services }) {
|
|
24
|
+
create({ adapter, config, conversationCapabilities = [ConversationSurfaceCapability.Review], services, }) {
|
|
23
25
|
if (services === undefined)
|
|
24
26
|
throw new Error('GitHub provider requires composed services');
|
|
25
27
|
const client = createGitHubClient(config.token ?? resolveGitHubCliToken());
|
|
@@ -38,6 +40,7 @@ export const gitHubProviderDefinition = {
|
|
|
38
40
|
intake: config.intake,
|
|
39
41
|
conclusion: services.conclusion,
|
|
40
42
|
conversations: services.conversations,
|
|
43
|
+
conversationCapabilities,
|
|
41
44
|
});
|
|
42
45
|
return {
|
|
43
46
|
adapter,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { CommandContext } from '@atolis-hq/eventing';
|
|
2
|
+
import { ActivityOutcomeKind } from '../../activities/index.js';
|
|
3
|
+
import { type WorkflowInstanceId } from '../contracts/identifiers.js';
|
|
4
|
+
import type { WorkflowInstanceView } from '../contracts/views.js';
|
|
5
|
+
import { ConversationSurfaceCapability } from '../contracts/vocabulary.js';
|
|
6
|
+
export declare const BuiltInConversationCommands: ("/approved" | "/accepted" | "/changes" | "/retry" | "/restart" | "/extend")[];
|
|
7
|
+
export type SurfaceCapability = ConversationSurfaceCapability;
|
|
8
|
+
export type ConversationCommandSurface = {
|
|
9
|
+
readonly capabilities: readonly SurfaceCapability[];
|
|
10
|
+
};
|
|
11
|
+
export declare function conversationCommand(body: string): string | null;
|
|
12
|
+
export declare function applyBuiltInConversationCommand(input: {
|
|
13
|
+
readonly command: string;
|
|
14
|
+
readonly capabilities: readonly SurfaceCapability[];
|
|
15
|
+
readonly workflows: readonly WorkflowInstanceView[];
|
|
16
|
+
readonly context: CommandContext;
|
|
17
|
+
readonly acceptSignal: (id: WorkflowInstanceId, outcome: typeof ActivityOutcomeKind.Done | typeof ActivityOutcomeKind.Rejected) => Promise<unknown>;
|
|
18
|
+
readonly retry: (id: WorkflowInstanceId) => Promise<unknown>;
|
|
19
|
+
readonly restart: (id: WorkflowInstanceId) => Promise<unknown>;
|
|
20
|
+
readonly extend: (id: WorkflowInstanceId) => Promise<unknown>;
|
|
21
|
+
readonly resumeChanges: (id: WorkflowInstanceId) => Promise<unknown>;
|
|
22
|
+
}): Promise<boolean>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ActivityOutcomeKind } from '../../activities/index.js';
|
|
2
|
+
import { ConversationBuiltInCommand, ConversationSurfaceCapability, } from '../contracts/vocabulary.js';
|
|
3
|
+
import { isGroupBudgetExtensionEligible, isOperatorRetryEligible, selectOperatorRetryTarget, } from '../domain/operator-retry-policy.js';
|
|
4
|
+
export const BuiltInConversationCommands = Object.values(ConversationBuiltInCommand);
|
|
5
|
+
export function conversationCommand(body) {
|
|
6
|
+
const line = body.trim().split('\n', 1)[0]?.trim().toLowerCase() ?? '';
|
|
7
|
+
return /^\/[a-z][a-z0-9-]*(?:\s.*)?$/.test(line) ? line.split(/\s/, 1)[0] : null;
|
|
8
|
+
}
|
|
9
|
+
// The command matrix is intentionally explicit so future built-ins cannot silently inherit power.
|
|
10
|
+
// eslint-disable-next-line complexity
|
|
11
|
+
export async function applyBuiltInConversationCommand(input) {
|
|
12
|
+
if (!BuiltInConversationCommands.includes(input.command))
|
|
13
|
+
return false;
|
|
14
|
+
if (!input.capabilities.includes(ConversationSurfaceCapability.Review) &&
|
|
15
|
+
!input.capabilities.includes(ConversationSurfaceCapability.Operator))
|
|
16
|
+
return false;
|
|
17
|
+
if (input.command === ConversationBuiltInCommand.Approved ||
|
|
18
|
+
input.command === ConversationBuiltInCommand.Accepted ||
|
|
19
|
+
input.command === ConversationBuiltInCommand.Changes) {
|
|
20
|
+
const outcome = input.command === ConversationBuiltInCommand.Changes
|
|
21
|
+
? ActivityOutcomeKind.Rejected
|
|
22
|
+
: ActivityOutcomeKind.Done;
|
|
23
|
+
for (const workflow of input.workflows) {
|
|
24
|
+
if (workflow.waitingFor !== undefined)
|
|
25
|
+
await input.acceptSignal(workflow.workflowInstanceId, outcome);
|
|
26
|
+
else if (input.command === ConversationBuiltInCommand.Changes)
|
|
27
|
+
await input.resumeChanges(workflow.workflowInstanceId);
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
if (input.command === ConversationBuiltInCommand.Retry) {
|
|
32
|
+
const target = selectOperatorRetryTarget(input.workflows);
|
|
33
|
+
if (target !== undefined)
|
|
34
|
+
await input.retry(target.workflowInstanceId);
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
const primary = input.workflows.find((workflow) => workflow.parentWorkflowInstanceId === undefined);
|
|
38
|
+
if (primary === undefined)
|
|
39
|
+
return true;
|
|
40
|
+
if (input.command === ConversationBuiltInCommand.Restart && isOperatorRetryEligible(primary))
|
|
41
|
+
await input.restart(primary.workflowInstanceId);
|
|
42
|
+
if (input.command === ConversationBuiltInCommand.Extend &&
|
|
43
|
+
isGroupBudgetExtensionEligible(primary))
|
|
44
|
+
await input.extend(primary.workflowInstanceId);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
@@ -24,6 +24,16 @@ export class OrchestrationRepository {
|
|
|
24
24
|
return { sequence: loaded.sequence, view: loaded.view };
|
|
25
25
|
}
|
|
26
26
|
async append(id, sequence, drafts) {
|
|
27
|
+
// Command and processor recovery may replay a completed append. Event IDs are
|
|
28
|
+
// deterministic from the command causation, so acknowledge that exact intent
|
|
29
|
+
// instead of appending a second copy after a crash between append and receipt.
|
|
30
|
+
const existing = await this.journal.readStream(workflowInstanceStream(workflowInstanceId(id)));
|
|
31
|
+
const existingIds = new Set(existing.map((event) => event.event.eventId));
|
|
32
|
+
if (drafts.every((draft) => existingIds.has(draft.eventId)))
|
|
33
|
+
return existing
|
|
34
|
+
.filter((event) => drafts.some((draft) => draft.eventId === event.event.eventId))
|
|
35
|
+
.map(decodeOrchestrationEvent)
|
|
36
|
+
.filter(isWorkflowEvent);
|
|
27
37
|
const events = await this.journal.appendToStream(workflowInstanceStream(workflowInstanceId(id)), sequence, drafts);
|
|
28
38
|
return events.map(decodeOrchestrationEvent).filter(isWorkflowEvent);
|
|
29
39
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { CommandContext, EventJournal, ProjectionStore } from '@atolis-hq/eventing';
|
|
2
2
|
import type { ActivationId, ActivityOutcome } from '../../activities/index.js';
|
|
3
|
-
import type
|
|
3
|
+
import { type WorkItemId, type WorkService } from '../../work/index.js';
|
|
4
4
|
import type { StartWorkflowInstance } from '../contracts/commands.js';
|
|
5
5
|
import type { CompiledWorkflow, TransitionTarget } from '../contracts/config.js';
|
|
6
6
|
import type { ChildWorkflowRequest, OrchestrationSignal, SignalExpectation, SupplementalActivityRequest } from '../contracts/events.js';
|
|
7
7
|
import { type SignalName, type WorkflowInstanceId } from '../contracts/identifiers.js';
|
|
8
8
|
import type { WorkflowInstanceView } from '../contracts/views.js';
|
|
9
9
|
import { AdvanceWorkflow } from './advance-workflow.js';
|
|
10
|
+
import { type SurfaceCapability } from './conversation-command.js';
|
|
10
11
|
export declare class OrchestrationService {
|
|
11
12
|
private readonly startWorkflow;
|
|
12
13
|
private readonly acceptActivityOutcome;
|
|
@@ -19,6 +20,7 @@ export declare class OrchestrationService {
|
|
|
19
20
|
private readonly claims;
|
|
20
21
|
private readonly journal;
|
|
21
22
|
private readonly definitions;
|
|
23
|
+
private readonly work;
|
|
22
24
|
constructor(journal: EventJournal, work: WorkService, definitions: Readonly<Record<string, CompiledWorkflow>>, projections?: ProjectionStore);
|
|
23
25
|
start(command: StartWorkflowInstance, context: CommandContext): Promise<WorkflowInstanceView>;
|
|
24
26
|
requestChild(request: ChildWorkflowRequest & {
|
|
@@ -35,6 +37,13 @@ export declare class OrchestrationService {
|
|
|
35
37
|
setAcceptSignalOperationCoordinator(coordinator: OperationCoordinator): void;
|
|
36
38
|
setWatchChildCancellation(cancellation: WatchChildCancellation): void;
|
|
37
39
|
requestSupplementalActivity(workflowInstanceId: WorkflowInstanceId, request: SupplementalActivityRequest, context: CommandContext): Promise<WorkflowInstanceView>;
|
|
40
|
+
applyConversationCommand(workItemId: WorkItemId, input: {
|
|
41
|
+
readonly body: string;
|
|
42
|
+
readonly actorId: string;
|
|
43
|
+
readonly capabilities: readonly SurfaceCapability[];
|
|
44
|
+
/** The adapter authenticated and authorized the actor for command control. */
|
|
45
|
+
readonly authorized: boolean;
|
|
46
|
+
}, context: CommandContext): Promise<boolean>;
|
|
38
47
|
markActivationStarted(workflowInstanceId: WorkflowInstanceId, activationId: ActivationId, context: CommandContext): Promise<WorkflowInstanceView | null>;
|
|
39
48
|
validateActivationDispatch(workflowInstanceId: WorkflowInstanceId, context: CommandContext): Promise<boolean>;
|
|
40
49
|
block(workflowInstanceId: WorkflowInstanceId, reason: string, context: CommandContext): Promise<WorkflowInstanceView | null>;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { WorkStatus } from '../../work/index.js';
|
|
1
2
|
import { selectOrchestrationEvent } from '../contracts/event-decoder.js';
|
|
2
3
|
import { OrchestrationEventType } from '../contracts/events.js';
|
|
3
|
-
import { workflowInstanceId as toWorkflowInstanceId, } from '../contracts/identifiers.js';
|
|
4
|
+
import { commandName, workflowInstanceId as toWorkflowInstanceId, } from '../contracts/identifiers.js';
|
|
4
5
|
import { childOrchestrationGroupStream, workflowInstanceStream } from '../contracts/streams.js';
|
|
5
6
|
import { ApprovalAuthorityKind } from '../contracts/vocabulary.js';
|
|
6
7
|
import { isGroupBudgetExtensionEligible } from '../domain/operator-retry-policy.js';
|
|
7
8
|
import { AcceptActivityOutcome } from './accept-activity-outcome.js';
|
|
8
9
|
import { AcceptSignal } from './accept-signal.js';
|
|
9
10
|
import { AdvanceWorkflow } from './advance-workflow.js';
|
|
11
|
+
import { applyBuiltInConversationCommand, conversationCommand, } from './conversation-command.js';
|
|
10
12
|
import { CoordinationClaims } from './coordination-claims.js';
|
|
11
13
|
import { GroupBudgetRecorder } from './group-budget-recorder.js';
|
|
12
14
|
import { workflowsByWorkItemProjection } from './orchestration-projection.js';
|
|
@@ -27,9 +29,11 @@ export class OrchestrationService {
|
|
|
27
29
|
claims;
|
|
28
30
|
journal;
|
|
29
31
|
definitions;
|
|
32
|
+
work;
|
|
30
33
|
constructor(journal, work, definitions, projections) {
|
|
31
34
|
this.projections = projections;
|
|
32
35
|
this.journal = journal;
|
|
36
|
+
this.work = work;
|
|
33
37
|
const repository = new OrchestrationRepository(journal);
|
|
34
38
|
this.claims = new CoordinationClaims(journal);
|
|
35
39
|
this.definitions = new WorkflowDefinitionRegistry(journal, projections, definitions);
|
|
@@ -66,6 +70,49 @@ export class OrchestrationService {
|
|
|
66
70
|
requestSupplementalActivity(workflowInstanceId, request, context) {
|
|
67
71
|
return this.advanceWorkflow.requestSupplementalActivity(workflowInstanceId, request, context);
|
|
68
72
|
}
|
|
73
|
+
async applyConversationCommand(workItemId, input, context) {
|
|
74
|
+
const command = conversationCommand(input.body);
|
|
75
|
+
if (command === null)
|
|
76
|
+
return false;
|
|
77
|
+
if (!input.authorized)
|
|
78
|
+
return false;
|
|
79
|
+
const work = await this.work.get(workItemId);
|
|
80
|
+
if (work === null || work.deleted || work.frozen || work.state !== WorkStatus.Open)
|
|
81
|
+
return false;
|
|
82
|
+
// A command is causally adjacent to its conversation entry; projections may lag
|
|
83
|
+
// the journal, so command application must read the authoritative workflow state.
|
|
84
|
+
const workflows = (await this.advanceWorkflow.listAll()).filter((workflow) => workflow.workItemId === workItemId);
|
|
85
|
+
// Configured commands deliberately win over built-ins, even when their spelling collides.
|
|
86
|
+
let supplemental = false;
|
|
87
|
+
for (const workflow of workflows) {
|
|
88
|
+
const definition = await this.definitions.resolve(workflow);
|
|
89
|
+
if (definition.commands[command] === undefined)
|
|
90
|
+
continue;
|
|
91
|
+
supplemental = true;
|
|
92
|
+
await this.requestSupplementalActivity(workflow.workflowInstanceId, { command: commandName(command) }, context);
|
|
93
|
+
}
|
|
94
|
+
if (supplemental)
|
|
95
|
+
return true;
|
|
96
|
+
return applyBuiltInConversationCommand({
|
|
97
|
+
command,
|
|
98
|
+
capabilities: input.capabilities,
|
|
99
|
+
workflows,
|
|
100
|
+
context,
|
|
101
|
+
acceptSignal: (id, outcome) => this.acceptSignal(id, {
|
|
102
|
+
kind: (workflows.find((workflow) => workflow.workflowInstanceId === id)?.waitingFor
|
|
103
|
+
?.signalKind ?? 'approved'),
|
|
104
|
+
outcome,
|
|
105
|
+
actorId: input.actorId,
|
|
106
|
+
actorDecision: { authorized: true, evidenceId: context.commandId },
|
|
107
|
+
providerEventId: context.commandId,
|
|
108
|
+
authority: { kind: ApprovalAuthorityKind.Human },
|
|
109
|
+
}, context),
|
|
110
|
+
retry: (id) => this.retryBlockedFailedStage(id, context),
|
|
111
|
+
restart: (id) => this.restartBlockedFailedStage(id, context),
|
|
112
|
+
extend: (id) => this.extendBlockedGroupBudget(id, { kind: ApprovalAuthorityKind.Human }, context),
|
|
113
|
+
resumeChanges: (id) => this.resumeBlockedStageForChanges(id, context),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
69
116
|
markActivationStarted(workflowInstanceId, activationId, context) {
|
|
70
117
|
return this.advanceWorkflow.markActivationStarted(workflowInstanceId, activationId, context);
|
|
71
118
|
}
|
|
@@ -37,6 +37,11 @@ declare const resourceTransitionConfigSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
37
37
|
}, z.core.$strict>;
|
|
38
38
|
then: z.ZodOptional<z.ZodString>;
|
|
39
39
|
}, z.core.$strict>]>;
|
|
40
|
+
export declare const surfaceCapabilitySchema: z.ZodEnum<{
|
|
41
|
+
"review-surface": "review-surface";
|
|
42
|
+
"chat-surface": "chat-surface";
|
|
43
|
+
"operator-surface": "operator-surface";
|
|
44
|
+
}>;
|
|
40
45
|
export declare const watchConfigSchema: z.ZodObject<{
|
|
41
46
|
id: z.ZodString;
|
|
42
47
|
while: z.ZodObject<{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { ApprovalAuthorityKind, WorkflowStatus } from './vocabulary.js';
|
|
2
|
+
import { ApprovalAuthorityKind, ConversationSurfaceCapability, WorkflowStatus, } from './vocabulary.js';
|
|
3
3
|
import { ActivityEventType, PullRequestCheckState, PullRequestState, } from '../../activities/index.js';
|
|
4
4
|
import { WorkspaceMode } from '../../execution/index.js';
|
|
5
5
|
import { MatchMode } from '../../kernel/index.js';
|
|
@@ -54,6 +54,11 @@ const resourceTransitionConfigSchema = z.union([
|
|
|
54
54
|
.strict(),
|
|
55
55
|
]);
|
|
56
56
|
const commandName = z.string().regex(/^\/[a-z][a-z0-9-]*$/);
|
|
57
|
+
export const surfaceCapabilitySchema = z.enum([
|
|
58
|
+
ConversationSurfaceCapability.Review,
|
|
59
|
+
ConversationSurfaceCapability.Chat,
|
|
60
|
+
ConversationSurfaceCapability.Operator,
|
|
61
|
+
]);
|
|
57
62
|
const canonicalEventName = z.string().regex(/^[a-z][a-z0-9-]*(\.[a-z][a-z0-9-]*)+$/);
|
|
58
63
|
const watchStatus = z.enum([WorkflowStatus.Active, WorkflowStatus.Waiting, WorkflowStatus.Blocked]);
|
|
59
64
|
const failingChecksWatchPredicateSchema = z
|
|
@@ -32,3 +32,18 @@ export declare const ApprovalAuthorityKind: {
|
|
|
32
32
|
readonly Watch: "watch";
|
|
33
33
|
};
|
|
34
34
|
export type ApprovalAuthorityKind = ValueOf<typeof ApprovalAuthorityKind>;
|
|
35
|
+
export declare const ConversationBuiltInCommand: {
|
|
36
|
+
readonly Approved: "/approved";
|
|
37
|
+
readonly Accepted: "/accepted";
|
|
38
|
+
readonly Changes: "/changes";
|
|
39
|
+
readonly Retry: "/retry";
|
|
40
|
+
readonly Restart: "/restart";
|
|
41
|
+
readonly Extend: "/extend";
|
|
42
|
+
};
|
|
43
|
+
export type ConversationBuiltInCommand = ValueOf<typeof ConversationBuiltInCommand>;
|
|
44
|
+
export declare const ConversationSurfaceCapability: {
|
|
45
|
+
readonly Review: "review-surface";
|
|
46
|
+
readonly Chat: "chat-surface";
|
|
47
|
+
readonly Operator: "operator-surface";
|
|
48
|
+
};
|
|
49
|
+
export type ConversationSurfaceCapability = ValueOf<typeof ConversationSurfaceCapability>;
|
|
@@ -29,3 +29,16 @@ export const ApprovalAuthorityKind = defineClosedVocabulary({
|
|
|
29
29
|
Auto: 'auto',
|
|
30
30
|
Watch: 'watch',
|
|
31
31
|
});
|
|
32
|
+
export const ConversationBuiltInCommand = defineClosedVocabulary({
|
|
33
|
+
Approved: '/approved',
|
|
34
|
+
Accepted: '/accepted',
|
|
35
|
+
Changes: '/changes',
|
|
36
|
+
Retry: '/retry',
|
|
37
|
+
Restart: '/restart',
|
|
38
|
+
Extend: '/extend',
|
|
39
|
+
});
|
|
40
|
+
export const ConversationSurfaceCapability = defineClosedVocabulary({
|
|
41
|
+
Review: 'review-surface',
|
|
42
|
+
Chat: 'chat-surface',
|
|
43
|
+
Operator: 'operator-surface',
|
|
44
|
+
});
|
|
@@ -10,6 +10,7 @@ export * from './application/resource-transition-reactor.js';
|
|
|
10
10
|
export * from './application/signal-reactor.js';
|
|
11
11
|
export * from './application/watch-reactor.js';
|
|
12
12
|
export * from './application/trigger-workflow-instance.js';
|
|
13
|
+
export * from './application/conversation-command.js';
|
|
13
14
|
export * from './application/workflow-definition-registry.js';
|
|
14
15
|
export * from './contracts/activity-outcome.js';
|
|
15
16
|
export * from './contracts/commands.js';
|
|
@@ -10,6 +10,7 @@ export * from './application/resource-transition-reactor.js';
|
|
|
10
10
|
export * from './application/signal-reactor.js';
|
|
11
11
|
export * from './application/watch-reactor.js';
|
|
12
12
|
export * from './application/trigger-workflow-instance.js';
|
|
13
|
+
export * from './application/conversation-command.js';
|
|
13
14
|
export * from './application/workflow-definition-registry.js';
|
|
14
15
|
export * from './contracts/activity-outcome.js';
|
|
15
16
|
export * from './contracts/commands.js';
|
|
@@ -24,7 +24,7 @@ export const usage = [
|
|
|
24
24
|
' wake run resolve <run-id> --failed --reason <message>',
|
|
25
25
|
' wake api Run the target API surface',
|
|
26
26
|
' wake sandbox-entrypoint Run the sandbox resident entrypoint',
|
|
27
|
-
' wake self-update Safely update a source
|
|
27
|
+
' wake self-update Safely update Wake from npm or a source checkout',
|
|
28
28
|
'',
|
|
29
29
|
'Getting started:',
|
|
30
30
|
' 1. wake init ./wake-home',
|