codex-session-insights 0.2.0 → 0.2.1
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/lib/llm-insights.js +83 -7
- package/lib/report.js +85 -21
- package/package.json +1 -1
package/lib/llm-insights.js
CHANGED
|
@@ -276,24 +276,27 @@ const SECTION_DEFS = [
|
|
|
276
276
|
contextKind: 'project_areas',
|
|
277
277
|
schemaName: 'codex_project_areas',
|
|
278
278
|
schema: PROJECT_AREAS_SCHEMA,
|
|
279
|
-
prompt: `Analyze this Codex usage data and identify
|
|
279
|
+
prompt: `Analyze this Codex usage data and identify the user's main workstreams.
|
|
280
280
|
|
|
281
281
|
RESPOND WITH ONLY A VALID JSON OBJECT:
|
|
282
282
|
{
|
|
283
283
|
"areas": [
|
|
284
|
-
{"name": "Area name", "session_count": 0, "description": "2-3 sentences
|
|
284
|
+
{"name": "Area name", "session_count": 0, "description": "2-3 sentences describing the workstream, its recurring tasks, and why it matters."}
|
|
285
285
|
]
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
-
Include 4
|
|
288
|
+
Include 3-4 areas. Skip Codex self-hosting/meta work unless it is a dominant project area.
|
|
289
289
|
|
|
290
290
|
Guardrails:
|
|
291
291
|
- Use concrete project or workstream names, not generic labels like "coding" or "development"
|
|
292
292
|
- Base areas on repeated evidence across summaries, not one-off threads
|
|
293
293
|
- Prefer project + task framing over tool-centric framing
|
|
294
|
-
- Group related tasks into a coherent workstream instead of listing each task separately
|
|
295
|
-
-
|
|
296
|
-
-
|
|
294
|
+
- Group related tasks into a coherent long-running workstream instead of listing each task separately
|
|
295
|
+
- Prefer fewer, broader areas that still feel accurate over a more complete but fragmented list
|
|
296
|
+
- Do not turn recent sub-tasks, bugfixes, or cleanup passes into separate areas unless they clearly form their own repeated stream
|
|
297
|
+
- Each description should read like a workstream summary, not a changelog
|
|
298
|
+
- Mention representative tasks, artifacts, or decisions so the area feels concrete without enumerating every thread
|
|
299
|
+
- Keep the focus on what the user was trying to accomplish; mention Codex only lightly when it clarifies the shape of the work`,
|
|
297
300
|
},
|
|
298
301
|
{
|
|
299
302
|
name: 'interaction_style',
|
|
@@ -311,9 +314,12 @@ RESPOND WITH ONLY A VALID JSON OBJECT:
|
|
|
311
314
|
|
|
312
315
|
Guardrails:
|
|
313
316
|
- Focus on stable interaction patterns, not isolated moments
|
|
314
|
-
- Talk about how the user scopes work,
|
|
317
|
+
- Talk about how the user scopes work, redirects goals, sets acceptance bars, or trusts execution
|
|
318
|
+
- Prefer evidence from user requests, follow-up corrections, repeated constraints, and outcome patterns over implementation telemetry
|
|
315
319
|
- Do not infer user preference from Codex's default tool mix or harness behavior; high exec/tool usage can reflect the agent's operating style rather than the user's instructions
|
|
316
320
|
- Treat shell usage, file reads, and verification commands as weak evidence unless the user explicitly asked for that working style
|
|
321
|
+
- Do not infer style from repository type, documentation volume, or language mix alone
|
|
322
|
+
- Avoid turning a single repo's workflow shape into a personality claim about the user
|
|
317
323
|
- If evidence is mixed, describe the tension instead of forcing one clean story`,
|
|
318
324
|
},
|
|
319
325
|
{
|
|
@@ -360,6 +366,7 @@ Include 3 friction categories with 2 examples each.
|
|
|
360
366
|
Guardrails:
|
|
361
367
|
- Separate model-side friction from user/workflow-side friction when useful
|
|
362
368
|
- Examples must be concrete and tied to the supplied evidence
|
|
369
|
+
- Treat overlap or concurrency metrics as weak supporting evidence unless the summaries or friction details also show real switching pain
|
|
363
370
|
- Do not invent root causes that are not visible in the data`,
|
|
364
371
|
},
|
|
365
372
|
{
|
|
@@ -401,6 +408,9 @@ Guardrails:
|
|
|
401
408
|
- Suggest only actions that clearly connect to repeated evidence
|
|
402
409
|
- Avoid generic advice like "give more context" unless it is overwhelmingly justified
|
|
403
410
|
- Prefer changes with strong leverage: repo memory, repeatable workflows, automation, or parallelism
|
|
411
|
+
- Do not recommend first-time adoption of AGENTS.md, Skills, codex exec, Sub-agents, or MCP Servers when the capability_adoption evidence shows the user already uses them in a moderate or strong way
|
|
412
|
+
- When a capability is already adopted, suggest a deeper refinement or a tighter operating pattern instead of basic adoption
|
|
413
|
+
- Distinguish "you should start using this" from "you should formalize or deepen how you already use this"
|
|
404
414
|
- Write AGENTS.md additions as directly pasteable instruction lines, not commentary about instructions
|
|
405
415
|
- Make feature examples immediately usable; avoid placeholders like "insert your repo path here" unless unavoidable
|
|
406
416
|
- Make usage pattern suggestions sound like concrete next actions the user can try today, not abstract best practices`,
|
|
@@ -989,6 +999,8 @@ function buildInsightContext(report, threadSummaries, facets) {
|
|
|
989
999
|
),
|
|
990
1000
|
).slice(0, MAX_USER_INSTRUCTIONS)
|
|
991
1001
|
|
|
1002
|
+
const capabilityAdoption = summarizeCapabilityAdoption(report, threadSummaries, facets)
|
|
1003
|
+
|
|
992
1004
|
return {
|
|
993
1005
|
metadata: {
|
|
994
1006
|
generated_at: report.metadata.generatedAt,
|
|
@@ -1025,6 +1037,7 @@ function buildInsightContext(report, threadSummaries, facets) {
|
|
|
1025
1037
|
friction,
|
|
1026
1038
|
success,
|
|
1027
1039
|
},
|
|
1040
|
+
capability_adoption: capabilityAdoption,
|
|
1028
1041
|
session_summaries: sortedFacets.slice(0, MAX_CONTEXT_FACETS).map(facet => ({
|
|
1029
1042
|
thread_id: facet.threadId,
|
|
1030
1043
|
title: truncateForContext(facet.title, 80),
|
|
@@ -1051,6 +1064,69 @@ function buildInsightContext(report, threadSummaries, facets) {
|
|
|
1051
1064
|
}
|
|
1052
1065
|
}
|
|
1053
1066
|
|
|
1067
|
+
function summarizeCapabilityAdoption(report, threadSummaries, facets) {
|
|
1068
|
+
const textByThread = new Map()
|
|
1069
|
+
for (const thread of threadSummaries) {
|
|
1070
|
+
textByThread.set(
|
|
1071
|
+
thread.id,
|
|
1072
|
+
[thread.title, thread.firstUserMessage]
|
|
1073
|
+
.map(value => String(value || ''))
|
|
1074
|
+
.join('\n')
|
|
1075
|
+
.toLowerCase(),
|
|
1076
|
+
)
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
for (const facet of facets) {
|
|
1080
|
+
const existing = textByThread.get(facet.threadId) || ''
|
|
1081
|
+
const facetText = [
|
|
1082
|
+
facet.underlying_goal,
|
|
1083
|
+
facet.brief_summary,
|
|
1084
|
+
...(facet.user_instructions || []),
|
|
1085
|
+
]
|
|
1086
|
+
.map(value => String(value || ''))
|
|
1087
|
+
.join('\n')
|
|
1088
|
+
.toLowerCase()
|
|
1089
|
+
textByThread.set(facet.threadId, `${existing}\n${facetText}`.trim())
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
const detectMentionedThreads = regex => {
|
|
1093
|
+
let count = 0
|
|
1094
|
+
for (const text of textByThread.values()) {
|
|
1095
|
+
if (regex.test(text)) count += 1
|
|
1096
|
+
}
|
|
1097
|
+
return count
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
const totalThreads = Math.max(1, Number(report.metadata.threadCount || threadSummaries.length || 0))
|
|
1101
|
+
const signals = {
|
|
1102
|
+
agents_md: detectMentionedThreads(/\bagents\.md\b/i),
|
|
1103
|
+
skills: detectMentionedThreads(/\bskills?\b/i),
|
|
1104
|
+
codex_exec: detectMentionedThreads(/\bcodex exec\b/i),
|
|
1105
|
+
subagents: Number(report.summary.sessionsUsingTaskAgent || 0),
|
|
1106
|
+
mcp_servers: Number(report.summary.sessionsUsingMcp || 0),
|
|
1107
|
+
web_search: Number(report.summary.sessionsUsingWebSearch || 0),
|
|
1108
|
+
web_fetch: Number(report.summary.sessionsUsingWebFetch || 0),
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
return Object.fromEntries(
|
|
1112
|
+
Object.entries(signals).map(([key, count]) => [
|
|
1113
|
+
key,
|
|
1114
|
+
{
|
|
1115
|
+
count,
|
|
1116
|
+
status: classifyCapabilityAdoption(count, totalThreads),
|
|
1117
|
+
},
|
|
1118
|
+
]),
|
|
1119
|
+
)
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
function classifyCapabilityAdoption(count, totalThreads) {
|
|
1123
|
+
const share = Number(count || 0) / Math.max(1, Number(totalThreads || 0))
|
|
1124
|
+
if (count >= 10 || share >= 0.25) return 'strong'
|
|
1125
|
+
if (count >= 4 || share >= 0.1) return 'moderate'
|
|
1126
|
+
if (count > 0) return 'light'
|
|
1127
|
+
return 'none'
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1054
1130
|
function buildAtAGlancePrompt(context, insights) {
|
|
1055
1131
|
return `You are writing an "At a Glance" summary for a Codex usage insights report.
|
|
1056
1132
|
|
package/lib/report.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import os from 'node:os'
|
|
1
2
|
import path from 'node:path'
|
|
2
3
|
import { promises as fs } from 'node:fs'
|
|
3
4
|
|
|
@@ -6,6 +7,9 @@ export function buildReport(threadSummaries, options = {}) {
|
|
|
6
7
|
const modelCounts = {}
|
|
7
8
|
const toolCounts = {}
|
|
8
9
|
const commandKindCounts = {}
|
|
10
|
+
const capabilityCounts = {}
|
|
11
|
+
const outcomeCounts = {}
|
|
12
|
+
const sessionTypeCounts = {}
|
|
9
13
|
const toolFailureCounts = {}
|
|
10
14
|
const activeHourCounts = {}
|
|
11
15
|
const responseTimes = []
|
|
@@ -81,8 +85,20 @@ export function buildReport(threadSummaries, options = {}) {
|
|
|
81
85
|
if (thread.usesMcp) sessionsUsingMcp += 1
|
|
82
86
|
if (thread.usesWebSearch) sessionsUsingWebSearch += 1
|
|
83
87
|
if (thread.usesWebFetch) sessionsUsingWebFetch += 1
|
|
88
|
+
if (thread.filesModified > 0) increment(capabilityCounts, 'Repo edits')
|
|
89
|
+
if (thread.gitCommits > 0 || thread.gitPushes > 0) increment(capabilityCounts, 'Git activity')
|
|
84
90
|
}
|
|
85
91
|
|
|
92
|
+
for (const facet of options.facets || []) {
|
|
93
|
+
if (facet.outcome) increment(outcomeCounts, facet.outcome)
|
|
94
|
+
if (facet.session_type) increment(sessionTypeCounts, facet.session_type)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (sessionsUsingTaskAgent > 0) increment(capabilityCounts, 'Sub-agents', sessionsUsingTaskAgent)
|
|
98
|
+
if (sessionsUsingMcp > 0) increment(capabilityCounts, 'MCP servers', sessionsUsingMcp)
|
|
99
|
+
if (sessionsUsingWebSearch > 0) increment(capabilityCounts, 'Web search', sessionsUsingWebSearch)
|
|
100
|
+
if (sessionsUsingWebFetch > 0) increment(capabilityCounts, 'Web fetch', sessionsUsingWebFetch)
|
|
101
|
+
|
|
86
102
|
const sortedThreads = [...threadSummaries].sort(
|
|
87
103
|
(a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt),
|
|
88
104
|
)
|
|
@@ -130,6 +146,9 @@ export function buildReport(threadSummaries, options = {}) {
|
|
|
130
146
|
models: topEntries(modelCounts, 10),
|
|
131
147
|
tools: topEntries(toolCounts, 12),
|
|
132
148
|
commandKinds: topEntries(commandKindCounts, 12),
|
|
149
|
+
capabilities: topEntries(capabilityCounts, 8),
|
|
150
|
+
outcomes: topEntries(outcomeCounts, 8),
|
|
151
|
+
sessionTypes: topEntries(sessionTypeCounts, 8),
|
|
133
152
|
toolFailures: topEntries(toolFailureCounts, 8),
|
|
134
153
|
toolErrorCategories: topEntries(toolErrorCategoryCounts, 8),
|
|
135
154
|
activeHours: buildHourSeries(activeHourCounts),
|
|
@@ -176,13 +195,13 @@ export function renderTerminalSummary(report) {
|
|
|
176
195
|
lines.push(`${report.metadata.dateRange.start} -> ${report.metadata.dateRange.end}`)
|
|
177
196
|
}
|
|
178
197
|
lines.push('')
|
|
179
|
-
lines.push(`${text.topTools}:`)
|
|
180
|
-
for (const item of report.charts.tools.slice(0, 5)) {
|
|
181
|
-
lines.push(` ${item.label}: ${item.value}`)
|
|
182
|
-
}
|
|
183
|
-
lines.push('')
|
|
184
198
|
lines.push(`${text.topProjects}:`)
|
|
185
199
|
for (const item of report.charts.projects.slice(0, 5)) {
|
|
200
|
+
lines.push(` ${formatProjectLabel(item.label)}: ${item.value}`)
|
|
201
|
+
}
|
|
202
|
+
lines.push('')
|
|
203
|
+
lines.push(`${text.modelMix}:`)
|
|
204
|
+
for (const item of report.charts.models.slice(0, 5)) {
|
|
186
205
|
lines.push(` ${item.label}: ${item.value}`)
|
|
187
206
|
}
|
|
188
207
|
return lines.join('\n')
|
|
@@ -193,9 +212,11 @@ function renderHtmlReport(report) {
|
|
|
193
212
|
const insights = report.insights
|
|
194
213
|
if (insights && !insights.__lang) insights.__lang = report.metadata.language
|
|
195
214
|
const analysisUsage = report.analysisUsage || null
|
|
196
|
-
const
|
|
197
|
-
const
|
|
198
|
-
const
|
|
215
|
+
const topProjects = renderBarList(report.charts.projects, { formatLabel: formatProjectLabel })
|
|
216
|
+
const modelMix = renderBarList(report.charts.models)
|
|
217
|
+
const sessionTypes = renderBarList(report.charts.sessionTypes)
|
|
218
|
+
const outcomes = renderBarList(report.charts.outcomes)
|
|
219
|
+
const capabilitySignals = renderBarList(report.charts.capabilities)
|
|
199
220
|
const toolFailures = renderBarList(report.charts.toolFailures)
|
|
200
221
|
const toolErrorCategories = renderBarList(report.charts.toolErrorCategories)
|
|
201
222
|
const activeHours = renderHourHistogram(report.charts.activeHours)
|
|
@@ -639,7 +660,7 @@ function renderHtmlReport(report) {
|
|
|
639
660
|
<section class="hero">
|
|
640
661
|
<span class="eyebrow">${escapeHtml(text.eyebrow)}</span>
|
|
641
662
|
<h1>${escapeHtml(text.reportTitle)}</h1>
|
|
642
|
-
<p class="meta">${escapeHtml(text.generatedLabel)} ${escapeHtml(report.metadata.generatedAt)} ${escapeHtml(text.generatedFrom)} ${report.metadata.threadCount} ${escapeHtml(text.substantiveThreads)} ${escapeHtml(text.inCodexHome)} ${escapeHtml(report.metadata.codexHome)}.</p>
|
|
663
|
+
<p class="meta">${escapeHtml(text.generatedLabel)} ${escapeHtml(report.metadata.generatedAt)} ${escapeHtml(text.generatedFrom)} ${report.metadata.threadCount} ${escapeHtml(text.substantiveThreads)} ${escapeHtml(text.inCodexHome)} ${escapeHtml(formatCodexHome(report.metadata.codexHome))}.</p>
|
|
643
664
|
<div class="summary-grid">
|
|
644
665
|
${renderStat(text.userMessages, formatNumber(report.summary.totalUserMessages))}
|
|
645
666
|
${renderStat(text.toolCalls, formatNumber(report.summary.totalToolCalls))}
|
|
@@ -672,17 +693,25 @@ function renderHtmlReport(report) {
|
|
|
672
693
|
${renderOnTheHorizon(insights)}
|
|
673
694
|
</div>
|
|
674
695
|
<aside class="side-column">
|
|
675
|
-
<section class="chart-panel">
|
|
676
|
-
<h2>${escapeHtml(text.topTools)}</h2>
|
|
677
|
-
${topTools}
|
|
678
|
-
</section>
|
|
679
696
|
<section class="chart-panel">
|
|
680
697
|
<h2>${escapeHtml(text.topProjects)}</h2>
|
|
681
698
|
${topProjects}
|
|
682
699
|
</section>
|
|
683
700
|
<section class="chart-panel">
|
|
684
|
-
<h2>${escapeHtml(text.
|
|
685
|
-
${
|
|
701
|
+
<h2>${escapeHtml(text.modelMix)}</h2>
|
|
702
|
+
${modelMix}
|
|
703
|
+
</section>
|
|
704
|
+
<section class="chart-panel">
|
|
705
|
+
<h2>${escapeHtml(text.sessionTypes)}</h2>
|
|
706
|
+
${sessionTypes}
|
|
707
|
+
</section>
|
|
708
|
+
<section class="chart-panel">
|
|
709
|
+
<h2>${escapeHtml(text.outcomes)}</h2>
|
|
710
|
+
${outcomes}
|
|
711
|
+
</section>
|
|
712
|
+
<section class="chart-panel">
|
|
713
|
+
<h2>${escapeHtml(text.capabilitySignals)}</h2>
|
|
714
|
+
${capabilitySignals}
|
|
686
715
|
</section>
|
|
687
716
|
<section class="chart-panel">
|
|
688
717
|
<h2>${escapeHtml(text.failureHotspots)}</h2>
|
|
@@ -1054,15 +1083,16 @@ function renderStat(label, value) {
|
|
|
1054
1083
|
return `<div class="stat"><div class="value">${escapeHtml(String(value))}</div><div>${escapeHtml(label)}</div></div>`
|
|
1055
1084
|
}
|
|
1056
1085
|
|
|
1057
|
-
function renderBarList(items) {
|
|
1086
|
+
function renderBarList(items, options = {}) {
|
|
1058
1087
|
if (!items.length) return '<p class="meta">No data available.</p>'
|
|
1088
|
+
const formatLabel = options.formatLabel || (value => value)
|
|
1059
1089
|
const maxValue = Math.max(...items.map(item => item.value), 1)
|
|
1060
1090
|
return `<div class="bar-list">${items
|
|
1061
1091
|
.map(
|
|
1062
1092
|
item => `
|
|
1063
1093
|
<div class="bar-row">
|
|
1064
1094
|
<div class="bar-label">
|
|
1065
|
-
<span>${escapeHtml(item.label)}</span>
|
|
1095
|
+
<span>${escapeHtml(formatLabel(item.label))}</span>
|
|
1066
1096
|
<strong>${formatNumber(item.value)}</strong>
|
|
1067
1097
|
</div>
|
|
1068
1098
|
<div class="bar-track"><div class="bar-fill" style="width:${Math.max(6, (item.value / maxValue) * 100)}%"></div></div>
|
|
@@ -1133,6 +1163,36 @@ function topEntries(map, limit) {
|
|
|
1133
1163
|
.map(([label, value]) => ({ label, value }))
|
|
1134
1164
|
}
|
|
1135
1165
|
|
|
1166
|
+
function formatCodexHome(value) {
|
|
1167
|
+
return formatDisplayPath(value, { tailSegments: 2, preferHomeAlias: true, ellipsis: false })
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
function formatProjectLabel(value) {
|
|
1171
|
+
return formatDisplayPath(value, { tailSegments: 2, preferHomeAlias: false, ellipsis: true })
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
function formatDisplayPath(value, options = {}) {
|
|
1175
|
+
const text = String(value || '').trim()
|
|
1176
|
+
if (!text) return '(unknown)'
|
|
1177
|
+
|
|
1178
|
+
const normalized = text.replace(/\\/g, '/')
|
|
1179
|
+
const home = os.homedir().replace(/\\/g, '/')
|
|
1180
|
+
if (options.preferHomeAlias !== false && normalized === home) return '~'
|
|
1181
|
+
if (options.preferHomeAlias !== false && normalized.startsWith(`${home}/`)) {
|
|
1182
|
+
return `~/${normalized.slice(home.length + 1)}`
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
const parts = normalized.split('/').filter(Boolean)
|
|
1186
|
+
const tailSegments = Math.max(1, Number(options.tailSegments || 2))
|
|
1187
|
+
if (parts.length <= tailSegments) {
|
|
1188
|
+
return normalized.startsWith('/') ? `/${parts.join('/')}` : parts.join('/')
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
const tail = parts.slice(-tailSegments).join('/')
|
|
1192
|
+
if (options.ellipsis === false) return tail
|
|
1193
|
+
return `…/${tail}`
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1136
1196
|
function buildHourSeries(hourMap) {
|
|
1137
1197
|
return Array.from({ length: 24 }, (_, hour) => ({
|
|
1138
1198
|
hour,
|
|
@@ -1244,9 +1304,11 @@ function getReportText(lang) {
|
|
|
1244
1304
|
filesModified: '修改文件',
|
|
1245
1305
|
toolErrors: '工具错误',
|
|
1246
1306
|
avgResponse: '平均响应',
|
|
1247
|
-
topTools: 'Top Tools',
|
|
1248
1307
|
topProjects: 'Top Projects',
|
|
1249
|
-
|
|
1308
|
+
modelMix: '模型分布',
|
|
1309
|
+
sessionTypes: '会话类型',
|
|
1310
|
+
outcomes: '结果分布',
|
|
1311
|
+
capabilitySignals: '能力信号',
|
|
1250
1312
|
failureHotspots: '失败热点',
|
|
1251
1313
|
errorCategories: '错误分类',
|
|
1252
1314
|
timeOfDay: '活跃时段',
|
|
@@ -1321,9 +1383,11 @@ function getReportText(lang) {
|
|
|
1321
1383
|
filesModified: 'Files Modified',
|
|
1322
1384
|
toolErrors: 'Tool Errors',
|
|
1323
1385
|
avgResponse: 'Avg Response',
|
|
1324
|
-
topTools: 'Top Tools',
|
|
1325
1386
|
topProjects: 'Top Projects',
|
|
1326
|
-
|
|
1387
|
+
modelMix: 'Model Mix',
|
|
1388
|
+
sessionTypes: 'Session Types',
|
|
1389
|
+
outcomes: 'Outcomes',
|
|
1390
|
+
capabilitySignals: 'Capability Signals',
|
|
1327
1391
|
failureHotspots: 'Failure Hotspots',
|
|
1328
1392
|
errorCategories: 'Error Categories',
|
|
1329
1393
|
timeOfDay: 'Time of Day',
|