@raidiant/notifai 9.0.0 → 9.1.0
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/CHANGELOG.md +7 -0
- package/dist/command-session.d.ts +14 -0
- package/dist/command-session.js +30 -0
- package/dist/command-session.js.map +1 -0
- package/dist/commands-doctor.js +17 -15
- package/dist/commands-doctor.js.map +1 -1
- package/dist/commands-guidance.js +3 -29
- package/dist/commands-guidance.js.map +1 -1
- package/dist/commands-harness-context.js +21 -1
- package/dist/commands-harness-context.js.map +1 -1
- package/dist/commands-hooks.d.ts +11 -1
- package/dist/commands-hooks.js +97 -141
- package/dist/commands-hooks.js.map +1 -1
- package/dist/commands-init.d.ts +2 -0
- package/dist/commands-init.js +41 -21
- package/dist/commands-init.js.map +1 -1
- package/dist/commands-send.d.ts +1 -0
- package/dist/commands-send.js +62 -14
- package/dist/commands-send.js.map +1 -1
- package/dist/commands-setup-proof.d.ts +4 -3
- package/dist/commands-setup-proof.js +29 -14
- package/dist/commands-setup-proof.js.map +1 -1
- package/dist/commands.d.ts +1 -1
- package/dist/commands.js +1 -1
- package/dist/commands.js.map +1 -1
- package/dist/guidance-render.d.ts +20 -0
- package/dist/guidance-render.js +42 -0
- package/dist/guidance-render.js.map +1 -0
- package/dist/hooks.d.ts +17 -2
- package/dist/hooks.js +59 -7
- package/dist/hooks.js.map +1 -1
- package/dist/opencode-plugin.js +16 -5
- package/dist/opencode-plugin.js.map +1 -1
- package/dist/program.js +21 -12
- package/dist/program.js.map +1 -1
- package/dist/readiness.d.ts +6 -0
- package/dist/readiness.js +37 -0
- package/dist/readiness.js.map +1 -1
- package/dist/send-attempts.d.ts +26 -0
- package/dist/send-attempts.js +139 -0
- package/dist/send-attempts.js.map +1 -0
- package/dist/session-activation.d.ts +4 -8
- package/dist/session-activation.js +25 -14
- package/dist/session-activation.js.map +1 -1
- package/package.json +1 -1
- package/src/command-session.ts +38 -0
- package/src/commands-doctor.ts +18 -28
- package/src/commands-guidance.ts +3 -38
- package/src/commands-harness-context.ts +24 -1
- package/src/commands-hooks.ts +217 -170
- package/src/commands-init.ts +46 -24
- package/src/commands-send.ts +70 -14
- package/src/commands-setup-proof.test.ts +72 -0
- package/src/commands-setup-proof.ts +33 -14
- package/src/commands.test.ts +209 -67
- package/src/commands.ts +1 -0
- package/src/guidance-render.ts +56 -0
- package/src/guidance.test.ts +28 -0
- package/src/hooks.test.ts +38 -7
- package/src/hooks.ts +69 -5
- package/src/install-hooks.test.ts +3 -3
- package/src/opencode-plugin.ts +16 -5
- package/src/program.ts +22 -12
- package/src/readiness.ts +44 -0
- package/src/send-attempts.test.ts +143 -0
- package/src/send-attempts.ts +188 -0
- package/src/session-activation.ts +34 -17
- package/src/skill.test.ts +12 -14
package/src/commands-doctor.ts
CHANGED
|
@@ -9,7 +9,6 @@ import { HARNESS_CAPABILITIES, HARNESS_LABELS } from './harnesses.js'
|
|
|
9
9
|
import { inspectHookAdapter } from './hook-adapter.js'
|
|
10
10
|
import {
|
|
11
11
|
readLiveProjectSessionPointers,
|
|
12
|
-
readMatchingProjectSessionPointer,
|
|
13
12
|
readProjectSessionPointer,
|
|
14
13
|
readSessionState,
|
|
15
14
|
} from './hooks.js'
|
|
@@ -33,6 +32,7 @@ import { inferInvocationContext } from './invocation-context.js'
|
|
|
33
32
|
import type { SkillScope } from './native-skills.js'
|
|
34
33
|
import {
|
|
35
34
|
firstBlocker,
|
|
35
|
+
readinessJson,
|
|
36
36
|
type Readiness,
|
|
37
37
|
type ReadinessRefresh,
|
|
38
38
|
type ReadinessState,
|
|
@@ -75,7 +75,9 @@ import {
|
|
|
75
75
|
SETUP_PROOF_STALE_MS,
|
|
76
76
|
observedCompanionReceipt,
|
|
77
77
|
readSetupProof,
|
|
78
|
+
setupProofProject,
|
|
78
79
|
setupProofIsStale,
|
|
80
|
+
setupProofApplies,
|
|
79
81
|
} from './commands-setup-proof.js'
|
|
80
82
|
import { skillReadiness } from './commands-skill.js'
|
|
81
83
|
|
|
@@ -542,12 +544,10 @@ async function setupProofState(
|
|
|
542
544
|
}
|
|
543
545
|
}
|
|
544
546
|
|
|
545
|
-
const
|
|
546
|
-
const
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
: companions.find((device) => device.device_id === proof.device_id)
|
|
550
|
-
if (proof === null || proof.project !== config.project.value || target === undefined) {
|
|
547
|
+
const project = setupProofProject(deps, config.project.value)
|
|
548
|
+
const proof = readSetupProof(deps, project)
|
|
549
|
+
const applies = setupProofApplies(proof, project, companions.map((device) => device.device_id))
|
|
550
|
+
if (!applies) {
|
|
551
551
|
return {
|
|
552
552
|
id: 'proof',
|
|
553
553
|
title: 'Delivery proof',
|
|
@@ -629,7 +629,7 @@ export async function doctorCommand(
|
|
|
629
629
|
if (flags.json || deps.io.interactive !== true) {
|
|
630
630
|
deps.io.out(
|
|
631
631
|
JSON.stringify(
|
|
632
|
-
{ ok, exit_code: ok ? EXIT.ok : EXIT.failed
|
|
632
|
+
{ ...readinessJson(readiness), ok, exit_code: ok ? EXIT.ok : EXIT.failed },
|
|
633
633
|
null,
|
|
634
634
|
2,
|
|
635
635
|
),
|
|
@@ -905,17 +905,11 @@ function hookChecks(deps: CommandDeps): HookCheck[] {
|
|
|
905
905
|
}),
|
|
906
906
|
})
|
|
907
907
|
if (activeInstallations.length > 0) {
|
|
908
|
-
const
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
deps.env,
|
|
914
|
-
(deps.now ?? Date.now)(),
|
|
915
|
-
active.sessionId,
|
|
916
|
-
active.harness,
|
|
917
|
-
)
|
|
918
|
-
if (pointer === null) {
|
|
908
|
+
const exactState = active.sessionId === undefined
|
|
909
|
+
? null
|
|
910
|
+
: readSessionState(active.sessionId, deps.env)
|
|
911
|
+
const activated = exactState?.harness === active.harness && exactState.last_prompt_at !== undefined
|
|
912
|
+
if (!activated) {
|
|
919
913
|
// The normal condition of hooks installed moments ago: the pointer
|
|
920
914
|
// appears when the harness next fires a hook, and no command can
|
|
921
915
|
// force that. Informational, so `init` walks on to the states it can
|
|
@@ -924,7 +918,7 @@ function hookChecks(deps: CommandDeps): HookCheck[] {
|
|
|
924
918
|
name: 'hooks (active session)',
|
|
925
919
|
ok: false,
|
|
926
920
|
informational: true,
|
|
927
|
-
detail: `active ${active.label} session has not published
|
|
921
|
+
detail: `active ${active.label} session has not published exact lifecycle state — send one ${active.label} prompt, then check again`,
|
|
928
922
|
remedy: {
|
|
929
923
|
summary: `send one ${active.label} prompt — its hook publishes the routing pointer`,
|
|
930
924
|
command: 'notifai doctor',
|
|
@@ -934,7 +928,7 @@ function hookChecks(deps: CommandDeps): HookCheck[] {
|
|
|
934
928
|
checks.push({
|
|
935
929
|
name: 'hooks (active session)',
|
|
936
930
|
ok: true,
|
|
937
|
-
detail: `the
|
|
931
|
+
detail: `the exact active ${active.label} session has published lifecycle state`,
|
|
938
932
|
})
|
|
939
933
|
}
|
|
940
934
|
}
|
|
@@ -1102,13 +1096,9 @@ function hookChecks(deps: CommandDeps): HookCheck[] {
|
|
|
1102
1096
|
? (readLiveProjectSessionPointers(deps.cwd, deps.env, (deps.now ?? Date.now)()).find(
|
|
1103
1097
|
(pointer) => pointer.harness === active.harness,
|
|
1104
1098
|
) ?? null)
|
|
1105
|
-
:
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
(deps.now ?? Date.now)(),
|
|
1109
|
-
active.sessionId,
|
|
1110
|
-
active.harness,
|
|
1111
|
-
)
|
|
1099
|
+
: readSessionState(active.sessionId, deps.env).harness === active.harness
|
|
1100
|
+
? { sessionId: active.sessionId, harness: active.harness }
|
|
1101
|
+
: null
|
|
1112
1102
|
const firedState = firedPointer === null ? null : readSessionState(firedPointer.sessionId, deps.env)
|
|
1113
1103
|
const promptFired = firedState?.last_prompt_at !== undefined
|
|
1114
1104
|
const stopFired = firedState?.last_stop_at !== undefined
|
package/src/commands-guidance.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
-
import { describeSource } from './config-schema.js'
|
|
4
3
|
import { EXIT, type CommandDeps } from './commands-core.js'
|
|
5
|
-
import {
|
|
6
|
-
GUIDANCE_TRUST_PREAMBLE,
|
|
7
|
-
SHIPPED_GUIDANCE,
|
|
8
|
-
shippedGuidanceTopic,
|
|
9
|
-
} from './guidance-content.js'
|
|
4
|
+
import { GUIDANCE_TRUST_PREAMBLE, SHIPPED_GUIDANCE, shippedGuidanceTopic } from './guidance-content.js'
|
|
10
5
|
import {
|
|
11
6
|
GUIDANCE_TOPIC_MAX_BYTES,
|
|
12
7
|
GUIDANCE_TOPIC_PATTERN,
|
|
@@ -14,36 +9,13 @@ import {
|
|
|
14
9
|
globalGuidanceDir,
|
|
15
10
|
personalProjectGuidanceDir,
|
|
16
11
|
resolveGuidance,
|
|
17
|
-
type GuidanceAuthority,
|
|
18
12
|
} from './guidance.js'
|
|
13
|
+
import { renderGuidance } from './guidance-render.js'
|
|
19
14
|
|
|
20
15
|
// ---------------------------------------------------------------------------
|
|
21
16
|
// guidance show / set / unset
|
|
22
17
|
// ---------------------------------------------------------------------------
|
|
23
18
|
|
|
24
|
-
/** How each authority is named in the marker above a topic. */
|
|
25
|
-
const AUTHORITY_LABEL: Record<GuidanceAuthority, string> = {
|
|
26
|
-
user: 'you',
|
|
27
|
-
repository: 'this repository',
|
|
28
|
-
shipped: 'shipped default',
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* The token that makes a marker line ours.
|
|
33
|
-
*
|
|
34
|
-
* Without it, a repository topic could open with a handwritten
|
|
35
|
-
* `<!-- when-to-notify · this machine · /Users/... -->` and everything after
|
|
36
|
-
* it would read as the User's own standing word. The marker therefore carries
|
|
37
|
-
* a fixed token, and any occurrence of that token inside file content is
|
|
38
|
-
* defanged below — so the only lines that can claim to be provenance are the
|
|
39
|
-
* ones this command wrote.
|
|
40
|
-
*/
|
|
41
|
-
const MARKER_TOKEN = 'notifai:guidance'
|
|
42
|
-
|
|
43
|
-
function markerSafe(content: string): string {
|
|
44
|
-
return content.replaceAll(MARKER_TOKEN, `${MARKER_TOKEN.replace(':', '-')} [not a provenance marker]`)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
19
|
/**
|
|
48
20
|
* The whole effective guidance, ready to be followed as read.
|
|
49
21
|
*
|
|
@@ -74,14 +46,7 @@ export function guidanceShowCommand(deps: CommandDeps, flags: { json?: boolean }
|
|
|
74
46
|
)
|
|
75
47
|
return EXIT.ok
|
|
76
48
|
}
|
|
77
|
-
|
|
78
|
-
const { path: filePath } = describeSource(topic.source)
|
|
79
|
-
const marker =
|
|
80
|
-
`<!-- ${MARKER_TOKEN} topic=${topic.name} from=${AUTHORITY_LABEL[topic.authority]}` +
|
|
81
|
-
`${filePath === null ? '' : ` file=${encodeURIComponent(filePath)}`} -->`
|
|
82
|
-
return `${marker}\n${markerSafe(topic.content).trimEnd()}`
|
|
83
|
-
})
|
|
84
|
-
deps.io.out([GUIDANCE_TRUST_PREAMBLE, ...blocks].join('\n\n'))
|
|
49
|
+
deps.io.out(renderGuidance(topics))
|
|
85
50
|
return EXIT.ok
|
|
86
51
|
}
|
|
87
52
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readLiveProjectSessionPointers } from './hooks.js'
|
|
1
|
+
import { readLiveProjectSessionPointers, readSessionState } from './hooks.js'
|
|
2
2
|
import { type Harness } from './install-hooks.js'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -96,6 +96,29 @@ export function resolveActiveHarness(
|
|
|
96
96
|
const first = candidates[0]
|
|
97
97
|
if (first === undefined) return { active: null, contested: [] }
|
|
98
98
|
if (candidates.length === 1) return { active: first, contested: [] }
|
|
99
|
+
|
|
100
|
+
// Exact lifecycle state is machine-global and survives a command moving to
|
|
101
|
+
// another linked checkout. Prefer it before the checkout-local compatibility
|
|
102
|
+
// pointer. A unique newest hook event proves which nested harness owns this
|
|
103
|
+
// shell; ties and missing evidence remain contested and fail closed.
|
|
104
|
+
const evidenced = candidates.flatMap((candidate) => {
|
|
105
|
+
if (candidate.sessionId === undefined) return []
|
|
106
|
+
const state = readSessionState(candidate.sessionId, env)
|
|
107
|
+
if (state.harness !== candidate.harness) return []
|
|
108
|
+
const activity = Math.max(
|
|
109
|
+
state.last_prompt_at ?? 0,
|
|
110
|
+
state.last_stop_at ?? 0,
|
|
111
|
+
state.activation_context_emitted_at ?? 0,
|
|
112
|
+
)
|
|
113
|
+
return activity > 0 ? [{ candidate, activity }] : []
|
|
114
|
+
})
|
|
115
|
+
if (evidenced.length > 0) {
|
|
116
|
+
evidenced.sort((left, right) => right.activity - left.activity)
|
|
117
|
+
if (evidenced.length === 1 || evidenced[0]!.activity > evidenced[1]!.activity) {
|
|
118
|
+
return { active: evidenced[0]!.candidate, contested: [] }
|
|
119
|
+
}
|
|
120
|
+
return { active: first, contested: candidates }
|
|
121
|
+
}
|
|
99
122
|
for (const pointer of readLiveProjectSessionPointers(cwd, env, now)) {
|
|
100
123
|
const owner = candidates.find(
|
|
101
124
|
(candidate) =>
|