Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2480,6 +2480,28 @@ Use `function invocation list FUNCTION_UUID` and `function invocation view FUNCT

Function, deployment, secret and invocation lists accept `--limit 1..100` and `--before UUID`. Deployment lists return metadata without source, including with `--json`. `function deployment view` exposes source with `--json`; secret reads always return metadata.

### Optional STOP confirmations

STOP acknowledgements are disabled by default and require beta access to enable.
Read the team UUID and revision before changing the preference:

```bash
polymorfa optout confirmations view
polymorfa optout confirmations summary --json
polymorfa optout confirmations update --team-id TEAM_UUID --revision 0 --no-enabled
```

To opt in, use the observed team/revision with `--enabled --locale en`,
`--locale pt-BR`, or `--locale es`. No language is inferred. Reads require a
team key with `campaigns:read`; saves require `campaigns:manage`. Disabling
remains available after beta withdrawal.

Preference saves never retry automatically. A stale revision returns an error;
read settings again after a conflict or uncertain save. The outcome summary
covers 30 days. Provider acceptance does not confirm delivery, and unknown
acknowledgements are never resent. Suppression remains recorded after any
acknowledgement failure.

### Call reaction and hand commands

`calls react CALL --connection-id CONNECTION --emoji '👍'` sends a reaction;
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"@modelcontextprotocol/server": "2.0.0",
"@napi-rs/keyring": "1.3.0",
"@oclif/core": "^4.13.5",
"@polymorfa/sdk": "0.1.0-dev.20261005230914",
"@polymorfa/sdk": "0.1.0-dev.20261005233122",
"jq-wasm": "3.0.0-jq-1.8.2",
"zod": "4.2.0"
},
Expand Down Expand Up @@ -300,6 +300,9 @@
"function:invocation": {
"description": "Invoke Functions once and inspect metadata receipts."
},
"optout:confirmations": {
"description": "Read and configure optional STOP acknowledgements (beta). Disabled by default."
},
"cloud": {
"description": "Official API Number operations (beta). Team enrollment and scopes apply."
},
Expand Down
17 changes: 17 additions & 0 deletions src/commands/optout/confirmations/summary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {BaseCommand} from '../../../base-command.js'
import {
readConfirmationSummary,
renderConfirmationSummary,
} from '../../../services/stop-confirmations.js'

export default class ConfirmationSummary extends BaseCommand {
static override description =
'Read 30-day STOP acknowledgement outcome counts. Provider acceptance does not confirm delivery.'
async run(): Promise<void> {
const {flags} = await this.parse(ConfirmationSummary)
const result = await readConfirmationSummary(
await this.platformClient(flags),
)
this.writeValue(result, renderConfirmationSummary(result.data), flags)
}
}
50 changes: 50 additions & 0 deletions src/commands/optout/confirmations/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {Flags} from '@oclif/core'
import {BaseCommand} from '../../../base-command.js'
import {
confirmationPreference,
renderConfirmationSettings,
updateConfirmationSettings,
} from '../../../services/stop-confirmations.js'

export default class ConfirmationSettingsUpdate extends BaseCommand {
static override description =
'Save an optional STOP acknowledgement preference using the observed team and revision. Enabling requires beta access and an explicit language. This write is never retried automatically; read settings after an uncertain save.'
static override examples = [
'<%= config.bin %> <%= command.id %> --team-id TEAM_UUID --revision 0 --no-enabled',
'<%= config.bin %> <%= command.id %> --team-id TEAM_UUID --revision 1 --enabled --locale pt-BR',
]
static override flags = {
enabled: Flags.boolean({
allowNo: true,
required: true,
description:
'Explicitly enable acknowledgements or pass --no-enabled to disable. Suppression stays in place.',
}),
locale: Flags.string({
options: ['en', 'pt-BR', 'es'],
description:
'Acknowledgement language; required when enabling. No language is inferred.',
}),
revision: Flags.integer({
required: true,
min: 0,
max: 2147483646,
description:
'Observed settings revision from confirmations view. Stale revisions fail with 409.',
}),
'team-id': Flags.string({
required: true,
description:
'Team UUID returned by confirmations view; must match the authenticated team.',
}),
}
async run(): Promise<void> {
const {flags} = await this.parse(ConfirmationSettingsUpdate)
const body = confirmationPreference(flags)
const result = await updateConfirmationSettings(
await this.platformClient(flags),
body,
)
this.writeValue(result, renderConfirmationSettings(result.data), flags)
}
}
17 changes: 17 additions & 0 deletions src/commands/optout/confirmations/view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {BaseCommand} from '../../../base-command.js'
import {
readConfirmationSettings,
renderConfirmationSettings,
} from '../../../services/stop-confirmations.js'

export default class ConfirmationSettingsView extends BaseCommand {
static override description =
'Read the team’s optional STOP acknowledgement preference. Disabled by default; enabling requires beta access.'
async run(): Promise<void> {
const {flags} = await this.parse(ConfirmationSettingsView)
const result = await readConfirmationSettings(
await this.platformClient(flags),
)
this.writeValue(result, renderConfirmationSettings(result.data), flags)
}
}
154 changes: 154 additions & 0 deletions src/services/stop-confirmations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import {
PolymorfaValidationError,
type ClientInstance,
type StopConfirmationSettings,
type StopConfirmationSummary,
type UpdateStopConfirmationSettingsRequest,
type ResponseMetadata,
} from '@polymorfa/sdk'
import {z} from 'zod'

const Locale = z.enum(['en', 'pt-BR', 'es'])
const Preference = z
.object({
teamId: z.uuid(),
enabled: z.boolean(),
locale: Locale.nullable(),
revision: z.number().int().min(0).max(2147483646),
})
.strict()
.refine(
(value) => !value.enabled || value.locale !== null,
Comment thread
purpshell marked this conversation as resolved.
'Select --locale when enabling confirmations.',
)
const Settings = z
.object({
teamId: z.uuid(),
enabled: z.boolean(),
locale: Locale.nullable(),
revision: z.number().int().nonnegative(),
textRevision: z.literal(1),
acknowledgement: z.string().nullable(),
updatedAt: z.number().nullable(),
})
.strict()
const Summary = z
.object({
enabled: z.boolean(),
counts: z
.object({
queued: z.number().int().nonnegative(),
admitted: z.number().int().nonnegative(),
completed: z.number().int().nonnegative(),
failed: z.number().int().nonnegative(),
unknown: z.number().int().nonnegative(),
})
.strict(),
})
.strict()

type Client = ClientInstance<'organization'>
function parseResponse<T>(
schema: z.ZodType<T>,
value: unknown,
message: string,
): T {
const parsed = schema.safeParse(value)
if (!parsed.success)
throw new PolymorfaValidationError(message, {
code: 'invalid_confirmation_response',
})
return parsed.data
}
const UNEXPECTED_READ =
'The STOP confirmation response did not match the expected shape. Update the CLI.'
function result<T>(data: T, metadata: ResponseMetadata) {
return {
data,
metadata: {
attempts: metadata.attempts,
status: metadata.status,
...(metadata.requestId === undefined
? {}
: {requestId: metadata.requestId}),
...(metadata.apiVersion === undefined
? {}
: {apiVersion: metadata.apiVersion}),
},
}
}
export async function readConfirmationSettings(client: Client) {
const response = await client.optOuts.getConfirmationSettings()
return result(
parseResponse(Settings, response.data.data, UNEXPECTED_READ),
response.metadata,
)
}
export async function readConfirmationSummary(client: Client) {
const response = await client.optOuts.getConfirmationSummary()
return result(
parseResponse(Summary, response.data.data, UNEXPECTED_READ),
response.metadata,
)
}
export function confirmationPreference(flags: {
enabled: boolean
'team-id': string
revision: number
locale?: string | undefined
}): UpdateStopConfirmationSettingsRequest {
const parsed = Preference.safeParse({
teamId: flags['team-id'],
enabled: flags.enabled,
locale: flags.locale ?? null,
revision: flags.revision,
})
if (!parsed.success)
throw new PolymorfaValidationError(
'Invalid STOP confirmation preference. Check the team UUID, revision, and language.',
{code: 'invalid_confirmation_preference'},
)
return parsed.data as UpdateStopConfirmationSettingsRequest
}
export async function updateConfirmationSettings(
client: Client,
body: UpdateStopConfirmationSettingsRequest,
) {
const response = await client.optOuts.updateConfirmationSettings(body, {
maxNetworkRetries: 0,
})
// The save already ran; an unexpected body does not prove it failed.
return result(
parseResponse(
Settings,
response.data.data,
'The STOP confirmation response did not match the expected shape. Run `polymorfa optout confirmations view` to check whether the preference was saved.',
),
response.metadata,
)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
export function renderConfirmationSettings(
settings: StopConfirmationSettings,
): string {
return [
`Team\t${settings.teamId}`,
`Enabled\t${settings.enabled}`,
`Language\t${settings.locale ?? 'Not selected'}`,
`Revision\t${settings.revision}`,
`Acknowledgement\t${settings.acknowledgement ?? 'Not selected'}`,
].join('\n')
}
export function renderConfirmationSummary(
summary: StopConfirmationSummary,
): string {
return [
`Enabled\t${summary.enabled}`,
'Outcomes in the last 30 days',
`Queued\t${summary.counts.queued}`,
`Admitted\t${summary.counts.admitted}`,
`Provider accepted\t${summary.counts.completed}`,
`Skipped or rejected\t${summary.counts.failed}`,
`Unknown\t${summary.counts.unknown}`,
'Provider acceptance does not confirm delivery. Unknown outcomes must not be resent; suppression remains recorded.',
].join('\n')
}
Loading