@capybara-hq/kernel 0.1.0-rc.25 → 0.1.0-rc.26
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/admin/outbox-dead.service.d.ts +7 -3
- package/dist/admin/outbox-dead.service.js +17 -6
- package/dist/admin/overview.service.d.ts +6 -6
- package/dist/admin/overview.service.js +7 -7
- package/dist/background/wake-rules.d.ts +5 -0
- package/dist/background/wake-rules.js +16 -5
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/channels/channels.module.d.ts +9 -8
- package/dist/channels/channels.module.js +11 -10
- package/dist/channels/connections.service.d.ts +3 -3
- package/dist/channels/connections.service.js +6 -6
- package/dist/channels/deliver-job.d.ts +10 -3
- package/dist/channels/deliver-job.js +7 -3
- package/dist/channels/jobs.d.ts +11 -4
- package/dist/channels/jobs.js +18 -3
- package/dist/channels/outbound.d.ts +5 -2
- package/dist/channels/outbound.js +9 -2
- package/dist/conversations/conversations.controller.d.ts +4 -2
- package/dist/conversations/conversations.controller.js +4 -2
- package/dist/conversations/conversations.service.d.ts +37 -4
- package/dist/conversations/conversations.service.js +77 -20
- package/dist/conversations/events.d.ts +6 -0
- package/dist/conversations/events.js +6 -0
- package/dist/db/kernel-schema.d.ts +10 -1
- package/dist/devices/devices.service.d.ts +14 -6
- package/dist/devices/devices.service.js +20 -7
- package/dist/evals/evals.module.js +5 -1
- package/dist/evals/jobs.d.ts +14 -6
- package/dist/evals/jobs.js +21 -19
- package/dist/evals/runs.service.d.ts +5 -6
- package/dist/evals/runs.service.js +11 -33
- package/dist/infra/outbox/outbox.service.d.ts +6 -0
- package/dist/infra/outbox/outbox.service.js +51 -17
- package/dist/infra/outbox/registry.d.ts +52 -0
- package/dist/infra/outbox/registry.js +57 -0
- package/dist/local-devices/device-policy.service.d.ts +131 -26
- package/dist/local-devices/device-policy.service.js +341 -145
- package/dist/local-devices/machines.d.ts +17 -2
- package/dist/local-devices/machines.js +66 -5
- package/dist/local-devices/policy-op-jobs.d.ts +15 -0
- package/dist/local-devices/policy-op-jobs.js +12 -0
- package/dist/local-devices/policy-summary.d.ts +36 -3
- package/dist/local-devices/policy-summary.js +47 -1
- package/dist/orgs/orgs.service.js +4 -1
- package/dist/overrides.d.ts +4 -0
- package/dist/overrides.js +2 -0
- package/dist/platform/ops/conversations.ops.js +1 -1
- package/dist/runtime/maintenance.d.ts +6 -3
- package/dist/runtime/maintenance.js +21 -7
- package/dist/runtime/plugin-context.service.d.ts +9 -3
- package/dist/runtime/plugin-context.service.js +17 -5
- package/dist/runtime/run-context.service.d.ts +7 -3
- package/dist/runtime/run-context.service.js +12 -2
- package/dist/runtime/run-finish.d.ts +2 -2
- package/dist/runtime/run-finish.js +11 -6
- package/dist/runtime/run-jobs.d.ts +21 -4
- package/dist/runtime/run-jobs.js +29 -6
- package/dist/runtime/run-orphan-check.job.d.ts +14 -11
- package/dist/runtime/run-orphan-check.job.js +16 -11
- package/dist/runtime/run-queue.d.ts +23 -31
- package/dist/runtime/run-queue.js +62 -64
- package/dist/runtime/run-status.d.ts +12 -0
- package/dist/runtime/run-status.js +21 -0
- package/dist/runtime/run-worker.d.ts +10 -2
- package/dist/runtime/run-worker.js +44 -19
- package/dist/runtime/step-log.d.ts +6 -5
- package/dist/runtime/step-log.js +6 -5
- package/dist/runtime/usage-report.d.ts +3 -1
- package/dist/runtime/usage-report.js +4 -2
- package/dist/runtime/waits.d.ts +16 -6
- package/dist/runtime/waits.js +37 -15
- package/dist/schedules/scan.job.d.ts +6 -6
- package/dist/schedules/scan.job.js +6 -6
- package/dist/skills/catalog.d.ts +7 -3
- package/dist/skills/catalog.js +12 -2
- package/dist/tools/approvals.service.d.ts +96 -19
- package/dist/tools/approvals.service.js +300 -67
- package/dist/tools/builtins/sandbox/task-output.js +21 -6
- package/dist/tools/device-op-resolver.d.ts +40 -0
- package/dist/tools/device-op-resolver.js +121 -0
- package/dist/tools/network-asks.d.ts +1 -1
- package/dist/tools/network-asks.js +4 -3
- package/dist/tools/tools.module.js +4 -0
- package/package.json +2 -2
- package/src/migrations/0125_conversation_tree_stopped_at.sql +5 -0
package/dist/runtime/waits.js
CHANGED
|
@@ -607,6 +607,13 @@ let Waits = class Waits {
|
|
|
607
607
|
* background tasks of every covered sub-agent conversation stop, whether it was running or idle.
|
|
608
608
|
* Returns the number of runs asked to stop.
|
|
609
609
|
*
|
|
610
|
+
* `windowsUpTo` (a root run's id; only after that root run a person stopped ended,
|
|
611
|
+
* {@link stopDescendantsOfStopped}) narrows the stop to the runs of the budget windows opened up
|
|
612
|
+
* to that run (`runs.budget_run_id`, agent map spec §6): a run of a window a later root run
|
|
613
|
+
* opened (the person's next message, started meanwhile) is never stopped by the late call, and
|
|
614
|
+
* the background task sweep is left out (a stopped child's own end stops its tasks,
|
|
615
|
+
* {@link childFinished}).
|
|
616
|
+
*
|
|
610
617
|
* Lock order: nothing is held across runs. Each run's stop is its own transaction (or none), and
|
|
611
618
|
* one that writes the terminal state locks that run's conversation row, then its run row, the
|
|
612
619
|
* order of `StepLog.appendCounted` (conversation row, then the root's budget run row) and of
|
|
@@ -619,12 +626,22 @@ let Waits = class Waits {
|
|
|
619
626
|
if (ids.length === 0)
|
|
620
627
|
return 0;
|
|
621
628
|
const level = new Map(tree.map((c) => [c.id, c.level]));
|
|
622
|
-
|
|
629
|
+
let q = this.db
|
|
623
630
|
.selectFrom("runs")
|
|
624
631
|
.select(["id", "conversation_id", "status"])
|
|
625
632
|
.where("conversation_id", "in", ids)
|
|
626
|
-
.where("status", "in", ACTIVE_RUN_STATUSES)
|
|
627
|
-
|
|
633
|
+
.where("status", "in", ACTIVE_RUN_STATUSES);
|
|
634
|
+
const upTo = opts.windowsUpTo;
|
|
635
|
+
if (upTo !== undefined) {
|
|
636
|
+
// a window is opened by a root run without a budget run of its own (`budgetWindowOf`)
|
|
637
|
+
q = q.where("budget_run_id", "in", (eb) => eb
|
|
638
|
+
.selectFrom("runs as w")
|
|
639
|
+
.select("w.id")
|
|
640
|
+
.where("w.conversation_id", "=", conversationId)
|
|
641
|
+
.where("w.budget_run_id", "is", null)
|
|
642
|
+
.where("w.created_at", "<=", (eb2) => eb2.selectFrom("runs as s").select("s.created_at").where("s.id", "=", upTo)));
|
|
643
|
+
}
|
|
644
|
+
const runs = (await q.execute()).sort((a, b) => (level.get(a.conversation_id) ?? 0) - (level.get(b.conversation_id) ?? 0));
|
|
628
645
|
for (const r of runs) {
|
|
629
646
|
try {
|
|
630
647
|
// a stopped wait's card updates are owed by its transaction's appends (outbox spec §8.1)
|
|
@@ -639,34 +656,39 @@ let Waits = class Waits {
|
|
|
639
656
|
// all of each conversation's tasks, as a stopped child's run end does (`childFinished`), with
|
|
640
657
|
// which this is idempotent (only running tasks are stopped; `stoppedBy: "subagent_end"`
|
|
641
658
|
// never wakes). A root conversation is skipped (`stopSubagentTasks`); never throws.
|
|
642
|
-
|
|
659
|
+
if (upTo === undefined) {
|
|
660
|
+
await Promise.all(ids.map((id) => this.background.stopSubagentTasks(id)));
|
|
661
|
+
}
|
|
643
662
|
return runs.length;
|
|
644
663
|
}
|
|
645
664
|
/**
|
|
646
|
-
* After a
|
|
647
|
-
*
|
|
648
|
-
*
|
|
649
|
-
*
|
|
650
|
-
*
|
|
651
|
-
*
|
|
665
|
+
* After a run's terminal commit (its worker): a run a person stopped (`run_aborted` with an
|
|
666
|
+
* account and no `detail`; {@link stopTree} or a person's stop of the root) stops the active runs
|
|
667
|
+
* below its conversation as well, so a descendant it dispatched after the tree stop read the tree
|
|
668
|
+
* still stops (dispatch needs its run `running`, so after its terminal commit the set is final).
|
|
669
|
+
* A root's run stops only the runs of the budget windows up to it (`windowsUpTo`): the person's
|
|
670
|
+
* next message may already have started a new run with sub-agents of its own (agent map spec §6,
|
|
671
|
+
* changed in acceptance 2026-11-13). Any other end leaves its children running (§5.4). Returns
|
|
672
|
+
* the number of runs asked to stop.
|
|
652
673
|
*/
|
|
653
674
|
async stopDescendantsOfStopped(runId) {
|
|
654
675
|
const ev = await this.db
|
|
655
676
|
.selectFrom("runs as r")
|
|
656
677
|
.innerJoin("conversation_events as e", (j) => j.onRef("e.conversation_id", "=", "r.conversation_id").onRef("e.run_id", "=", "r.id"))
|
|
657
678
|
.innerJoin("conversations as c", "c.id", "r.conversation_id")
|
|
658
|
-
.select(["r.conversation_id", "e.payload"])
|
|
679
|
+
.select(["r.conversation_id", "c.kind", "e.payload"])
|
|
659
680
|
.where("r.id", "=", runId)
|
|
660
|
-
|
|
661
|
-
// reaching here (a redelivered job, the failure path) stops nothing
|
|
662
|
-
.where("c.kind", "=", "subagent")
|
|
681
|
+
.where("c.kind", "in", ["chat", "subagent"])
|
|
663
682
|
.where("r.status", "=", "aborted")
|
|
664
683
|
.where("e.type", "=", "run_aborted")
|
|
665
684
|
.executeTakeFirst();
|
|
666
685
|
const p = ev?.payload;
|
|
667
686
|
if (!ev || typeof p?.byAccountId !== "string" || p.detail !== undefined)
|
|
668
687
|
return 0;
|
|
669
|
-
return this.stopTree(ev.conversation_id, p.byAccountId, {
|
|
688
|
+
return this.stopTree(ev.conversation_id, p.byAccountId, {
|
|
689
|
+
includeSelf: false,
|
|
690
|
+
...(ev.kind === "chat" && { windowsUpTo: runId }),
|
|
691
|
+
});
|
|
670
692
|
}
|
|
671
693
|
/**
|
|
672
694
|
* Stops one active sub-agent run: a `waiting` one through its own {@link abortWaiting}, a
|
|
@@ -15,9 +15,9 @@ import { ScheduledTaskStore } from "./store.js";
|
|
|
15
15
|
* transaction owes `sched-fire-<taskRunId>` (transactional outbox spec §8.1), so a written tick is
|
|
16
16
|
* never left unfired and the scan needs no repair of lost fire jobs (a fire delivered too late
|
|
17
17
|
* marks its tick missed itself, `ScheduledTaskFirer`), and the run's unfired settle check (a fire
|
|
18
|
-
* job that failed for good before its claim: the tick is missed, `job-ids.ts`). A task whose stored
|
|
19
|
-
* math cannot evaluate is paused (`invalid_schedule`) instead of failing the
|
|
20
|
-
* error fails the pass (the next minute retries) and pauses nothing.
|
|
18
|
+
* job that failed for good before its claim: the tick is missed, `job-ids.ts`). A task whose stored
|
|
19
|
+
* trigger the schedule math cannot evaluate is paused (`invalid_schedule`) instead of failing the
|
|
20
|
+
* scan; a database error fails the pass (the next minute retries) and pauses nothing.
|
|
21
21
|
*/
|
|
22
22
|
export declare class ScheduledTaskScanJob {
|
|
23
23
|
private readonly kdb;
|
|
@@ -33,9 +33,9 @@ export declare class ScheduledTaskScanJob {
|
|
|
33
33
|
/** One scan as of `now` (tests pass their own); returns how many due tasks it handled. */
|
|
34
34
|
scanAt(now: Date): Promise<number>;
|
|
35
35
|
/**
|
|
36
|
-
* One due task in its own transaction (a task whose trigger cannot be evaluated rolls back
|
|
37
|
-
*
|
|
38
|
-
* when no task is due.
|
|
36
|
+
* One due task in its own transaction (a task whose trigger cannot be evaluated rolls back alone
|
|
37
|
+
* and is paused, so it cannot block the others): locked with `SKIP LOCKED`, so replicas take
|
|
38
|
+
* different tasks. False when no task is due.
|
|
39
39
|
*/
|
|
40
40
|
private scanOne;
|
|
41
41
|
/** A task the scan cannot advance stops being due: paused with `invalid_schedule`. */
|
|
@@ -46,9 +46,9 @@ class TaskScanFailed extends Error {
|
|
|
46
46
|
* transaction owes `sched-fire-<taskRunId>` (transactional outbox spec §8.1), so a written tick is
|
|
47
47
|
* never left unfired and the scan needs no repair of lost fire jobs (a fire delivered too late
|
|
48
48
|
* marks its tick missed itself, `ScheduledTaskFirer`), and the run's unfired settle check (a fire
|
|
49
|
-
* job that failed for good before its claim: the tick is missed, `job-ids.ts`). A task whose stored
|
|
50
|
-
* math cannot evaluate is paused (`invalid_schedule`) instead of failing the
|
|
51
|
-
* error fails the pass (the next minute retries) and pauses nothing.
|
|
49
|
+
* job that failed for good before its claim: the tick is missed, `job-ids.ts`). A task whose stored
|
|
50
|
+
* trigger the schedule math cannot evaluate is paused (`invalid_schedule`) instead of failing the
|
|
51
|
+
* scan; a database error fails the pass (the next minute retries) and pauses nothing.
|
|
52
52
|
*/
|
|
53
53
|
let ScheduledTaskScanJob = class ScheduledTaskScanJob {
|
|
54
54
|
kdb;
|
|
@@ -96,9 +96,9 @@ let ScheduledTaskScanJob = class ScheduledTaskScanJob {
|
|
|
96
96
|
return total;
|
|
97
97
|
}
|
|
98
98
|
/**
|
|
99
|
-
* One due task in its own transaction (a task whose trigger cannot be evaluated rolls back
|
|
100
|
-
*
|
|
101
|
-
* when no task is due.
|
|
99
|
+
* One due task in its own transaction (a task whose trigger cannot be evaluated rolls back alone
|
|
100
|
+
* and is paused, so it cannot block the others): locked with `SKIP LOCKED`, so replicas take
|
|
101
|
+
* different tasks. False when no task is due.
|
|
102
102
|
*/
|
|
103
103
|
async scanOne(now, settings) {
|
|
104
104
|
const t = this.db;
|
package/dist/skills/catalog.d.ts
CHANGED
|
@@ -156,13 +156,17 @@ export declare class SkillsCatalogService {
|
|
|
156
156
|
constructor(db: KernelDb, eventLog: EventLog, log: KernelLogger, txHost: TransactionHost, config: KernelConfigService);
|
|
157
157
|
/**
|
|
158
158
|
* Records the run start's snapshot (see the class comment). `opts.log`: the run's logger
|
|
159
|
-
* (default the kernel logger).
|
|
159
|
+
* (default the kernel logger). False: the run had ended (a stop got there first), so nothing was
|
|
160
|
+
* appended and the run must not go on.
|
|
160
161
|
*/
|
|
161
162
|
record(input: EffectiveSkillsInput & {
|
|
162
163
|
runId: string;
|
|
163
164
|
}, opts?: {
|
|
164
165
|
log?: Pick<Logger, "warn" | "error">;
|
|
165
|
-
}): Promise<
|
|
166
|
-
/**
|
|
166
|
+
}): Promise<boolean>;
|
|
167
|
+
/**
|
|
168
|
+
* Under the conversation row lock, resolves the skills and appends a changed snapshot; `ended`
|
|
169
|
+
* (nothing appended) when the run is terminal under the locks.
|
|
170
|
+
*/
|
|
167
171
|
private append;
|
|
168
172
|
}
|
package/dist/skills/catalog.js
CHANGED
|
@@ -18,6 +18,7 @@ import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
|
18
18
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
19
19
|
import { CLAUDE_CODE_TOOL_MAP } from "../runtime/context/claude-tools.js";
|
|
20
20
|
import { catalogLine, sameSnapshot, } from "../runtime/context/skills.js";
|
|
21
|
+
import { runEndedIn } from "../runtime/run-status.js";
|
|
21
22
|
import { agentOwnSkillIds, agentSkills, builtinSkillIds, builtinSkillPlugins, } from "./usable.js";
|
|
22
23
|
/**
|
|
23
24
|
* The skills in effect for this conversation now, sorted by name (code point order), as
|
|
@@ -264,16 +265,25 @@ let SkillsCatalogService = class SkillsCatalogService {
|
|
|
264
265
|
}
|
|
265
266
|
/**
|
|
266
267
|
* Records the run start's snapshot (see the class comment). `opts.log`: the run's logger
|
|
267
|
-
* (default the kernel logger).
|
|
268
|
+
* (default the kernel logger). False: the run had ended (a stop got there first), so nothing was
|
|
269
|
+
* appended and the run must not go on.
|
|
268
270
|
*/
|
|
269
271
|
async record(input, opts = {}) {
|
|
270
272
|
const toSeq = await this.append(input, opts.log ?? this.log);
|
|
273
|
+
if (toSeq === "ended")
|
|
274
|
+
return false;
|
|
271
275
|
if (toSeq !== null)
|
|
272
276
|
await this.eventLog.notify(input.conversationId, toSeq);
|
|
277
|
+
return true;
|
|
273
278
|
}
|
|
274
|
-
/**
|
|
279
|
+
/**
|
|
280
|
+
* Under the conversation row lock, resolves the skills and appends a changed snapshot; `ended`
|
|
281
|
+
* (nothing appended) when the run is terminal under the locks.
|
|
282
|
+
*/
|
|
275
283
|
async append(input, log) {
|
|
276
284
|
const last = await lockLatestCatalog(this.db.db, input.conversationId);
|
|
285
|
+
if (await runEndedIn(this.db.db, input.runId))
|
|
286
|
+
return "ended";
|
|
277
287
|
const all = await resolveSkills(this.db.db, input, log);
|
|
278
288
|
const resolved = forModel(all, input, log);
|
|
279
289
|
const budget = this.config.get().skills.catalogBudgetChars;
|
|
@@ -6,7 +6,7 @@ import { KernelDb } from "../infra/database/kernel-db.js";
|
|
|
6
6
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
7
7
|
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
8
8
|
import { LockService } from "../infra/lock/lock.service.js";
|
|
9
|
-
import { DevicePolicyService } from "../local-devices/device-policy.service.js";
|
|
9
|
+
import { DevicePolicyService, type PolicyOpResult } from "../local-devices/device-policy.service.js";
|
|
10
10
|
import { type OrgRole } from "../orgs/roles.js";
|
|
11
11
|
import { RunResume } from "../runtime/run-jobs.js";
|
|
12
12
|
import { SandboxService } from "../sandbox/sandbox.service.js";
|
|
@@ -47,7 +47,6 @@ export interface NetworkAskDetail {
|
|
|
47
47
|
};
|
|
48
48
|
/** The hosts the decision allowed (every approved decision writes them; none: nothing). */
|
|
49
49
|
grantedHosts?: string[];
|
|
50
|
-
/** What took effect: a run grant, the device's policy, or a run grant because the device did not apply it. */
|
|
51
50
|
/**
|
|
52
51
|
* What took effect: a run grant (`once`), the device's policy (`always`), a run grant because
|
|
53
52
|
* the device could not be reached or lacks the feature (`once_fallback`), or nothing because the
|
|
@@ -56,6 +55,22 @@ export interface NetworkAskDetail {
|
|
|
56
55
|
outcome?: "once" | "always" | "once_fallback" | "refused";
|
|
57
56
|
/** Why 「一直允许」 did not reach the device's policy. */
|
|
58
57
|
fallback?: "offline" | "refused" | "unsupported";
|
|
58
|
+
/**
|
|
59
|
+
* Set while the ask is still pending: the last 「一直允许」 whose device change was recorded only
|
|
60
|
+
* after a crash (transactional outbox spec §7.4) and did not take effect: `failed` (the device
|
|
61
|
+
* refused it, never received it or stopped while applying it) or `unknown` (the device did not
|
|
62
|
+
* answer; its next connection tells), with the reason. The owner may decide again.
|
|
63
|
+
*/
|
|
64
|
+
attempt?: NetworkAskAttempt;
|
|
65
|
+
}
|
|
66
|
+
/** Why a pending network ask's last 「一直允许」 did not decide it ({@link NetworkAskDetail.attempt}). */
|
|
67
|
+
export interface NetworkAskAttempt {
|
|
68
|
+
/**
|
|
69
|
+
* `applied_undecided`: the device applied the change, but the account that chose it can no
|
|
70
|
+
* longer decide this ask (someone else must).
|
|
71
|
+
*/
|
|
72
|
+
outcome: "failed" | "unknown" | "applied_undecided";
|
|
73
|
+
message: string;
|
|
59
74
|
}
|
|
60
75
|
/** A network ask's detail as the API shows it: without the kept command result. */
|
|
61
76
|
export type NetworkAskView = Omit<NetworkAskDetail, "result">;
|
|
@@ -189,7 +204,7 @@ export interface NetworkAskInput {
|
|
|
189
204
|
callId: string;
|
|
190
205
|
/** The device owner: the only person who may decide. */
|
|
191
206
|
requestedFor: string;
|
|
192
|
-
detail: Omit<NetworkAskDetail, "grantedHosts" | "outcome" | "fallback">;
|
|
207
|
+
detail: Omit<NetworkAskDetail, "grantedHosts" | "outcome" | "fallback" | "attempt">;
|
|
193
208
|
/** `["once", "always"]`, or `["once"]` when the device takes no remote grants. */
|
|
194
209
|
scopes: ("once" | "always")[];
|
|
195
210
|
}
|
|
@@ -223,7 +238,10 @@ export declare class ApprovalsService {
|
|
|
223
238
|
private readonly schedules;
|
|
224
239
|
/** A trial approval is kept only for a tool the task may hold as a grant (its agent's tools). */
|
|
225
240
|
private readonly registry;
|
|
226
|
-
/**
|
|
241
|
+
/**
|
|
242
|
+
* 「这台设备一直允许」 records the device change's intent, sends the `policy` op, then records
|
|
243
|
+
* its outcome with the decision.
|
|
244
|
+
*/
|
|
227
245
|
private readonly policies;
|
|
228
246
|
/** One decision of a network ask at a time, across replicas (the op is sent before the transaction). */
|
|
229
247
|
readonly lockService: LockService;
|
|
@@ -234,8 +252,11 @@ export declare class ApprovalsService {
|
|
|
234
252
|
constructor(db: KernelDb, config: Pick<KernelConfigService, "get">, eventLog: Pick<EventLog, "appendIn" | "notify">, events: Pick<KernelEvents, "emit">, txHost: TransactionHost, schedules: Pick<ScheduledTaskStore, "addGrant">,
|
|
235
253
|
/** A trial approval is kept only for a tool the task may hold as a grant (its agent's tools). */
|
|
236
254
|
registry: Pick<ToolRegistry, "forAgent">,
|
|
237
|
-
/**
|
|
238
|
-
|
|
255
|
+
/**
|
|
256
|
+
* 「这台设备一直允许」 records the device change's intent, sends the `policy` op, then records
|
|
257
|
+
* its outcome with the decision.
|
|
258
|
+
*/
|
|
259
|
+
policies: Pick<DevicePolicyService, "begin" | "send" | "settleIn" | "summary">,
|
|
239
260
|
/** One decision of a network ask at a time, across replicas (the op is sent before the transaction). */
|
|
240
261
|
lockService: LockService,
|
|
241
262
|
/** A trial run's 「这次允许」 becomes a task grant only within the org's egress. */
|
|
@@ -272,28 +293,84 @@ export declare class ApprovalsService {
|
|
|
272
293
|
* A network ask's decision (network ask-to-allow spec §5.2), one at a time per ask across
|
|
273
294
|
* replicas: only the device owner (`requested_for`) decides, in a signed-in session (a personal
|
|
274
295
|
* access token is refused; the route already refuses service and embed tokens); the allowed
|
|
275
|
-
* hosts are a subset of those asked. 「这台设备一直允许」
|
|
276
|
-
*
|
|
277
|
-
* as a run grant with the reason, and when the device refuses (its owner takes no
|
|
278
|
-
* as `always` with outcome `refused` and nothing granted
|
|
279
|
-
*
|
|
280
|
-
*
|
|
296
|
+
* hosts are a subset of those asked. 「这台设备一直允许」 goes through the device change's intent
|
|
297
|
+
* ({@link decideAlways}): when the device cannot be reached or lacks the feature the decision is
|
|
298
|
+
* recorded as a run grant with the reason, and when the device refuses (its owner takes no
|
|
299
|
+
* remote grants) as `always` with outcome `refused` and nothing granted; the decision and the
|
|
300
|
+
* op's outcome are one transaction. Every decision resumes the run in its transaction and is
|
|
301
|
+
* announced after the commit; a change the device applied after the ask closed is audited
|
|
302
|
+
* (`late`) and answered with a 409 that says so.
|
|
281
303
|
*/
|
|
282
304
|
private decideNetwork;
|
|
283
|
-
/** `device.network_allowed` of a policy change that reached the device after the ask closed. */
|
|
284
|
-
private auditLateAllow;
|
|
285
|
-
/** The owner of a live device of the org (null: none, deleted, or of another org). */
|
|
286
|
-
private deviceOwnerOf;
|
|
287
305
|
/**
|
|
288
|
-
*
|
|
289
|
-
*
|
|
306
|
+
* 「这台设备一直允许」 (transactional outbox spec §7): the intent of the device change (naming the
|
|
307
|
+
* ask) is recorded, the `policy` op sent (no transaction open), then {@link settleNetworkOp}
|
|
308
|
+
* decides the ask and records the op's outcome in one transaction. When nothing could be sent
|
|
309
|
+
* (the device is offline, deleted, or its program lacks the feature) the decision is a run
|
|
310
|
+
* grant with the reason. The device's answer decides: applied → `always`; refused → `always`
|
|
311
|
+
* with outcome `refused`, nothing granted; no answer → a run grant (`once_fallback`, `offline`)
|
|
312
|
+
* while the op stays `unknown` for the machine's next hello or the resolver. A device that
|
|
313
|
+
* reconnects during the call and reports the op applied lets the resolver approve the ask as
|
|
314
|
+
* this owner first: that decision is returned (`toSeq` null: already announced), never a 409;
|
|
315
|
+
* when it granted other hosts than this call's (another of the owner's changes) and this call's
|
|
316
|
+
* change was not confirmed, `approval.already_decided` says so. A device deleted during the
|
|
317
|
+
* call is `device.not_found` (the op recorded unknown).
|
|
290
318
|
*/
|
|
291
|
-
private
|
|
319
|
+
private decideAlways;
|
|
320
|
+
/** Records only the op's outcome, in its own transaction (the ask is left as it is). */
|
|
321
|
+
private settleOpOnly;
|
|
322
|
+
/**
|
|
323
|
+
* The resolve transaction of a network ask's device change: the ask decided as `network` says
|
|
324
|
+
* ({@link decideTx}: lock order conversation → approval → run), then the op's outcome recorded
|
|
325
|
+
* (`DevicePolicyService.settleIn`: the machine's row). When the ask closed meanwhile (a reply,
|
|
326
|
+
* the expiry, a stop) only the op's outcome is recorded, and a change the device applied is
|
|
327
|
+
* audited `device.network_allowed` with `late: true` in the same transaction.
|
|
328
|
+
*/
|
|
329
|
+
private settleNetworkOp;
|
|
330
|
+
/**
|
|
331
|
+
* The op's outcome for an ask that is no longer pending (in the caller's transaction): recorded,
|
|
332
|
+
* and a change the device applied audited `device.network_allowed` `late` (the change stands:
|
|
333
|
+
* the owner chose it; no inverse change is sent). True when it was audited late. `undecided`:
|
|
334
|
+
* the ask is still pending but the op's account can no longer decide it; the allow is audited
|
|
335
|
+
* without `late` (it is not after the ask) and the caller gives the ask the reason.
|
|
336
|
+
*/
|
|
337
|
+
private settleClosedIn;
|
|
338
|
+
/** The live intent `opId` on machine `machineId` (null: settled already, or expired). */
|
|
339
|
+
private pendingOpOf;
|
|
340
|
+
/**
|
|
341
|
+
* The `device-op-resolve` job's outcome of an op that answers network ask `op.source.approvalId`
|
|
342
|
+
* (transactional outbox spec §7.4), in the resolver's transaction: under the ask's locks
|
|
343
|
+
* (conversation, then the approval) the op is read again (settled meanwhile: nothing to do).
|
|
344
|
+
* Applied, and the ask still pending → approved `always` by the op's account (`via` as it
|
|
345
|
+
* asked), the run resumed in this transaction as every decision does; the ask closed → audited
|
|
346
|
+
* `late`; still pending but the op's account can no longer decide it → the allow audited (not
|
|
347
|
+
* late) and the ask left pending with `applied_undecided`. Not applied (refused, never received,
|
|
348
|
+
* interrupted) or still unknown → the op's outcome recorded and the ask left pending with the
|
|
349
|
+
* reason (`detail.attempt`) and an `approval_updated` event so the card shows it: no person is
|
|
350
|
+
* there to fall back to a run grant (D2).
|
|
351
|
+
*/
|
|
352
|
+
settleFromResolver(ref: {
|
|
353
|
+
machineId: string;
|
|
354
|
+
opId: string;
|
|
355
|
+
approvalId: string;
|
|
356
|
+
}, result: PolicyOpResult): Promise<void>;
|
|
357
|
+
/**
|
|
358
|
+
* A pending network ask's reason (`detail.attempt`) and its `approval_updated` event (one per
|
|
359
|
+
* op and outcome), in the caller's transaction with the ask locked; notified after the commit.
|
|
360
|
+
*/
|
|
361
|
+
private attemptIn;
|
|
362
|
+
/** The owner of a live device of the org (null: none, deleted, or of another org). */
|
|
363
|
+
private deviceOwnerOf;
|
|
292
364
|
/** After the commit: the decision's events reach the page, the run's waits and IM delivery. */
|
|
293
365
|
private announceDecided;
|
|
294
366
|
/** An account's display name (null when unknown). */
|
|
295
367
|
private displayNameOf;
|
|
296
368
|
private decideIn;
|
|
369
|
+
/**
|
|
370
|
+
* A decision in the caller's transaction: lock order conversation → approval → run (the resume).
|
|
371
|
+
* Refuses (`approval.already_decided`, `approval.not_found`, …) before any write.
|
|
372
|
+
*/
|
|
373
|
+
private decideTx;
|
|
297
374
|
/**
|
|
298
375
|
* A trial run of a scheduled task (scheduled tasks spec §7): an approval its creator granted, in
|
|
299
376
|
* any scope, is also added to the task's grants with the approved definition's hash, so its
|